diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c137001d..87e52f05 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.10.0 +current_version = 0.13.0 tag = True sign_tags = True tag_message = evmone {new_version} diff --git a/.cicd/defaults.json b/.cicd/defaults.json new file mode 100644 index 00000000..deaa69b8 --- /dev/null +++ b/.cicd/defaults.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/.cicd/platforms.json b/.cicd/platforms.json new file mode 100644 index 00000000..2b0c8c46 --- /dev/null +++ b/.cicd/platforms.json @@ -0,0 +1,5 @@ +{ + "ubuntu22": { + "dockerfile": ".cicd/platforms/ubuntu22.Dockerfile" + } + } diff --git a/.cicd/platforms/ubuntu22.Dockerfile b/.cicd/platforms/ubuntu22.Dockerfile new file mode 100644 index 00000000..1b217157 --- /dev/null +++ b/.cicd/platforms/ubuntu22.Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:jammy +ENV TZ="America/New_York" +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y build-essential \ + cmake \ + gcc-11 \ + g++-11 \ + git diff --git a/.clang-tidy b/.clang-tidy index 66c9fd8a..3b6cf63b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,66 +1,72 @@ FormatStyle: file HeaderFilterRegex: 'lib/evmone/|test/state/' WarningsAsErrors: '*' -Checks: > - bugprone-*, - -bugprone-assignment-in-if-condition, - -bugprone-easily-swappable-parameters, - -bugprone-implicit-widening-of-multiplication-result, - -bugprone-unchecked-optional-access, - cert-dcl21-cpp, - cert-dcl50-cpp, - cert-dcl58-cpp, - cert-env33-c, - cert-err33-c, - cert-err34-c, - cert-err52-cpp, - cert-err60-cpp, - cert-flp30-c, - cert-mem57-cpp, - cert-msc50-cpp, - cert-msc51-cpp, - cert-oop57-cpp, - cert-oop58-cpp, - clang-analyzer-*, - cppcoreguidelines-*, - -cppcoreguidelines-avoid-c-arrays, - -cppcoreguidelines-avoid-goto, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-macro-usage, - -cppcoreguidelines-no-malloc, - -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-owning-memory, - -cppcoreguidelines-pro-bounds-array-to-pointer-decay, - -cppcoreguidelines-pro-bounds-constant-array-index, - -cppcoreguidelines-pro-bounds-pointer-arithmetic, - -cppcoreguidelines-pro-type-reinterpret-cast, - -cppcoreguidelines-pro-type-static-cast-downcast, - -cppcoreguidelines-pro-type-union-access, - -cppcoreguidelines-pro-type-vararg, - -cppcoreguidelines-special-member-functions, - google-global-names-in-headers, - google-runtime-int, - hicpp-exception-baseclass, - hicpp-multiway-paths-covered, - hicpp-no-assembler, - misc-*, - -misc-non-private-member-variables-in-classes, - modernize-*, - -modernize-avoid-c-arrays, - -modernize-use-trailing-return-type, - performance-*, - portability-*, - readability-*, - -readability-braces-around-statements, - -readability-else-after-return, - -readability-function-cognitive-complexity, - -readability-function-size, - -readability-identifier-length, - -readability-magic-numbers, - -readability-named-parameter, - -readability-qualified-auto, - -readability-uppercase-literal-suffix, +Checks: + - "bugprone-*" + - "-bugprone-assignment-in-if-condition" + - "-bugprone-easily-swappable-parameters" + - "-bugprone-implicit-widening-of-multiplication-result" + - "-bugprone-unchecked-optional-access" + - "cert-dcl21-cpp" + - "cert-dcl50-cpp" + - "cert-dcl58-cpp" + - "cert-env33-c" + - "cert-err33-c" + - "cert-err34-c" + - "cert-err52-cpp" + - "cert-err60-cpp" + - "cert-flp30-c" + - "cert-mem57-cpp" + - "cert-msc50-cpp" + - "cert-msc51-cpp" + - "cert-oop57-cpp" + - "cert-oop58-cpp" + - "clang-analyzer-*" + - "cppcoreguidelines-*" + - "-cppcoreguidelines-avoid-c-arrays" + - "-cppcoreguidelines-avoid-const-or-ref-data-members" + - "-cppcoreguidelines-avoid-goto" + - "-cppcoreguidelines-avoid-magic-numbers" + - "-cppcoreguidelines-avoid-non-const-global-variables" + - "-cppcoreguidelines-macro-usage" + - "-cppcoreguidelines-no-malloc" + - "-cppcoreguidelines-non-private-member-variables-in-classes" + - "-cppcoreguidelines-owning-memory" + - "-cppcoreguidelines-pro-bounds-array-to-pointer-decay" + - "-cppcoreguidelines-pro-bounds-constant-array-index" + - "-cppcoreguidelines-pro-bounds-pointer-arithmetic" + - "-cppcoreguidelines-pro-type-reinterpret-cast" + - "-cppcoreguidelines-pro-type-static-cast-downcast" + - "-cppcoreguidelines-pro-type-union-access" + - "-cppcoreguidelines-pro-type-vararg" + - "-cppcoreguidelines-special-member-functions" + - "google-global-names-in-headers" + - "google-runtime-int" + - "hicpp-exception-baseclass" + - "hicpp-multiway-paths-covered" + - "hicpp-no-assembler" + - "misc-*" + - "-misc-include-cleaner" + - "-misc-non-private-member-variables-in-classes" + - "-misc-use-anonymous-namespace" + - "modernize-*" + - "-modernize-avoid-c-arrays" + - "-modernize-use-trailing-return-type" + - "performance-*" + - "-performance-enum-size" + - "portability-*" + - "readability-*" + - "-readability-braces-around-statements" + - "-readability-else-after-return" + - "-readability-function-cognitive-complexity" + - "-readability-function-size" + - "-readability-identifier-length" + - "-readability-magic-numbers" + - "-readability-named-parameter" + - "-readability-qualified-auto" + # TODO: Check if removing inline affects clang optimized builds. + - "-readability-redundant-inline-specifier" + - "-readability-uppercase-literal-suffix" CheckOptions: readability-identifier-naming.ClassCase: CamelCase diff --git a/.github/workflows/build-tests.sh b/.github/workflows/build-tests.sh new file mode 100755 index 00000000..a3a66585 --- /dev/null +++ b/.github/workflows/build-tests.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# print and run a command +function ee() +{ + echo "$ $*" + eval "$@" +} + +# debug code +echo "CC='${CC}'" +echo "CXX='${CXX}'" +ee cmake --version + +# build +ee mkdir build +ee pushd build +ee cmake -DEVMONE_TESTING=ON -DBUILD_SHARED_LIBS=OFF .. +ee make -j "$(nproc)" + +# pack +ee popd +ee 'tar -czf build.tar.gz build/bin/*' + +echo "Done! - ${0##*/}" diff --git a/.github/workflows/evmone.md b/.github/workflows/evmone.md new file mode 100644 index 00000000..f5f79a85 --- /dev/null +++ b/.github/workflows/evmone.md @@ -0,0 +1,53 @@ +# EOS EVM evmone CI +This GitHub Actions workflow builds all emvone modules + +### Index +1. [Triggers](#triggers) +1. [Inputs](#inputs) +1. [Steps](#steps) +1. [Outputs](#outputs) +1. [See Also](#see-also) + +## Triggers +This GitHub action will run under the following circumstances: +1. When code is pushed. +1. Workflow dispatch event, a manual CI run, which can be triggered by the "Workflow Dispatch" button in the Actions tab of the GitHub repository, among other means. + +## Inputs +The inputs for this GitHub action are: +1. `GITHUB_TOKEN` - a GitHub Actions intrinsic used to access the repository and other public resources. +1. `TRUSTEVM_CI_APP_ID` - the app ID of the `trustevm-ci-submodule-checkout` GitHub App. +1. `TRUSTEVM_CI_APP_KEY` - the private key to the `trustevm-ci-submodule-checkout` GitHub App. +1. `upload-artifacts` - a boolean input that specifies whether or not to upload the artifacts of the build. The default value is `false`. This can be overridden in manual CI runs. + +These inputs are used in various steps of the workflow to perform actions such as authentication, downloading artifacts, configuring the build, and uploading artifacts. + +## Steps +This workflow performs the following steps: +1. Attach Documentation + 1. Checkout the repo with no submodules. + 1. Attach an annotation to the GitHub Actions build summary page containing CI documentation. +1. EOS EVM Siklworm Build + 1. Authenticate to the `trustevm-ci-submodule-checkout` GitHub app using the [AntelopeIO/github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) action to obtain an ephemeral token. + 1. Checkout the repo and submodules using the ephemeral token. + 1. Build using `cmake` and `make`. + 1. Upload the test binaries to GitHub Actions if the `upload-artifacts` input is set to `true`. + +## Outputs +This workflow produces the following outputs: +1. Build Artifacts - `build.tar.gz` containing the built artifacts of unit tests of the evmone project, if the `upload-artifacts` input is set to `true`. Note that only tests are included as we do not really care about other binaries for this repo. + +> 💾️ Build artifacts are only attached on-demand for this pipeline because they are >117 MB each, but we only get 2 GB of cumulative artifact storage in GitHub Actions while eos-evm is a private repo. Obtain artifacts by performing a manual build with `upload-artifacts` set to `true`. + +## See Also +- [github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) GitHub action +- [EOS EVM Documentation](../../README.md) + +For assistance with the CI system, please open an issue in this repo or reach out in the `#help-automation` channel via IM. + +*** +**_Legal notice_** +This document was generated in collaboration with ChatGPT from OpenAI, a machine learning algorithm or weak artificial intelligence (AI). At the time of this writing, the [OpenAI terms of service agreement](https://openai.com/terms) §3.a states: +> Your Content. You may provide input to the Services (“Input”), and receive output generated and returned by the Services based on the Input (“Output”). Input and Output are collectively “Content.” As between the parties and to the extent permitted by applicable law, you own all Input, and subject to your compliance with these Terms, OpenAI hereby assigns to you all its right, title and interest in and to Output. + +This notice is required in some countries. diff --git a/.github/workflows/evmone.yml b/.github/workflows/evmone.yml new file mode 100644 index 00000000..58104d1e --- /dev/null +++ b/.github/workflows/evmone.yml @@ -0,0 +1,143 @@ +name: EOS EVM evmone CI + +on: + push: + branches: + - main + - release/* + pull_request: + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + documentation: + name: Attach Documentation + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + fetch-depth: 1 + submodules: 'false' + + - name: Attach Documentation + run: cat .github/workflows/evmone.md >> $GITHUB_STEP_SUMMARY + + d: + name: Discover Platforms + runs-on: ubuntu-latest + outputs: + missing-platforms: ${{steps.discover.outputs.missing-platforms}} + p: ${{steps.discover.outputs.platforms}} + steps: + - name: Discover Platforms + id: discover + uses: AntelopeIO/discover-platforms-action@v1 + with: + platform-file: .cicd/platforms.json + password: ${{secrets.GITHUB_TOKEN}} + package-name: builders-evmone + + build-platforms: + name: Build Platforms + needs: d + if: needs.d.outputs.missing-platforms != '[]' + strategy: + fail-fast: false + matrix: + platform: ${{fromJSON(needs.d.outputs.missing-platforms)}} + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Login to Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{github.repository_owner}} + password: ${{secrets.GITHUB_TOKEN}} + - name: Build and push + uses: docker/build-push-action@v3 + with: + push: true + tags: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}} + file: ${{fromJSON(needs.d.outputs.p)[matrix.platform].dockerfile}} + + build: + name: EOS EVM evmone Build + needs: [d, build-platforms] + if: always() && needs.d.result == 'success' && (needs.build-platforms.result == 'success' || needs.build-platforms.result == 'skipped') + strategy: + fail-fast: false + matrix: + platform: [ ubuntu22 ] + runs-on: ubuntu-latest + container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}} + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: 'recursive' + token: ${{secrets.GITHUB_TOKEN}} + + - name: Build EOS EVM evmone + run: .github/workflows/build-tests.sh + env: + CC: gcc-11 + CXX: g++-11 + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: build.tar.gz + path: build.tar.gz + + unit-test: + name: EOS EVM evmone Unit Tests + needs: [d, build] + if: always() && needs.d.result == 'success' && needs.build.result == 'success' + strategy: + fail-fast: false + matrix: + platform: [ ubuntu22 ] + runs-on: ubuntu-latest + container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}} + env: + CC: gcc-11 + CXX: g++-11 + DCMAKE_BUILD_TYPE: 'Release' + + steps: + - name: Update Package Index & Upgrade Packages + run: | + apt-get update + apt-get upgrade -y + apt update + apt upgrade -y + + - name: Download EOS EVM evmone builddir + uses: actions/download-artifact@v4 + with: + name: build.tar.gz + + - name: Extract EOS EVM evmone builddir + id: evm-evmone-build + run: | + mkdir evmone + mv build.tar.gz evmone/ + pushd evmone + tar xvf build.tar.gz + pushd build + echo "EVM_EVMONE_BUILD=$(pwd)" >> "$GITHUB_OUTPUT" + popd + + - name: Test evmone + run: | + cd ${{ steps.evm-evmone-build.outputs.EVM_EVMONE_BUILD }}/bin/ + ./evmone-unittests diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dff642d..ae61e0fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,358 @@ Documentation of all notable changes to the **evmone** project. The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. +## [0.13.0] — 2024-09-23 + +This release adds BLS precompiles and a system contract for [Prague] +and improves the interpreter API. + +### Added + +- Implementation of all [EIP-2537] BLS precompiles, enabled in **Prague**: + [#984](https://github.com/ethereum/evmone/pull/984) + - uses [blst] [v0.3.13](https://github.com/supranational/blst/releases/tag/v0.3.13) library, + [#972](https://github.com/ethereum/evmone/pull/972) + [#986](https://github.com/ethereum/evmone/pull/986) + - `bls12_g1add` (`0x0b`) + [#982](https://github.com/ethereum/evmone/pull/982) + - `bls12_g1mul` (`0x0c`) + [#994](https://github.com/ethereum/evmone/pull/994) + - `bls12_g1msm` (`0x0d`) + [#1010](https://github.com/ethereum/evmone/pull/1010) + - `bls12_g2add` (`0x0e`) + [#995](https://github.com/ethereum/evmone/pull/995) + - `bls12_g2mul` (`0x0f`) + [#999](https://github.com/ethereum/evmone/pull/999) + - `bls12_g2msm` (`0x10`) + [#1010](https://github.com/ethereum/evmone/pull/1010) + - `bls12_pairing_check` (`0x11`) + [#1016](https://github.com/ethereum/evmone/pull/1016) + - `bls12_map_fp_to_g1` (`0x12`) + [#1012](https://github.com/ethereum/evmone/pull/1012) + - `bls12_map_fp2_to_g2` (`0x13`) + [#1012](https://github.com/ethereum/evmone/pull/1012) +- Implementation of KZG proof verification (aka "point evaluation") precompile from [EIP-4844]. + [#979](https://github.com/ethereum/evmone/pull/979) +- Implementation of [EIP-2935] "Serve historical block hashes from state". + [#953](https://github.com/ethereum/evmone/pull/953) + +### Changed + +- Refactor `system_call()` in preparation for more **Pectra** system contracts. + [#976](https://github.com/ethereum/evmone/pull/976) +- Improved Baseline code analysis API. + [#941](https://github.com/ethereum/evmone/pull/941) +- Provide execution states at VM object level and hide them from public API. + [#1005](https://github.com/ethereum/evmone/pull/1005) +- Requirements and dependencies updates: + - Support for 32-bit MSVC compiler has been dropped. + [#973](https://github.com/ethereum/evmone/pull/973) + - [intx] [v0.12.0](https://github.com/chfast/intx/releases/tag/v0.12.0) + [#985](https://github.com/ethereum/evmone/pull/985) +- External test suites: + - EEST EOF tests upgraded to [eip7692@v1.1.0](https://github.com/ethereum/execution-spec-tests/releases/tag/eip7692%40v1.1.0). + [#1025](https://github.com/ethereum/evmone/pull/1025) + - Added EEST tests for Pectra [pectra-devnet-3@v1.5.0](https://github.com/ethereum/execution-spec-tests/releases/tag/pectra-devnet-3%40v1.5.0) + [#997](https://github.com/ethereum/evmone/pull/997) + - [ethereum/tests] upgraded to [v14.1](https://github.com/ethereum/tests/releases/tag/v14.1). + [#980](https://github.com/ethereum/evmone/pull/980) + +### Fixed + +- Fixed EOF parsing bug allowing multiple subcontainer kinds in the header. + [#978](https://github.com/ethereum/evmone/pull/978) +- Ensure mandatory fields are included in the exported state tests. + [#993](https://github.com/ethereum/evmone/pull/993) +- Properly handle EOF additions in `ExecutionState::reset()`. + [#1004](https://github.com/ethereum/evmone/pull/1004) + +### Removed + +- The implementation of EOF's `TXCREATE` has been removed. It will be back when scheduled for a network upgrade. + [#992](https://github.com/ethereum/evmone/pull/992) + + +## [0.12.0] — 2024-08-08 + +This release is focused on the Prague upgrade and EOF. + +### Added + +- Added `evmone-precompiles-bench` tool to benchmark precompiles. + [#765](https://github.com/ethereum/evmone/pull/765) +- Added native implementations of the precompiled hash functions: + - RIPEMD160 + [#846](https://github.com/ethereum/evmone/pull/846) + - BLAKE2bf + [#857](https://github.com/ethereum/evmone/pull/857) + - SHA256 + [#924](https://github.com/ethereum/evmone/pull/924) +- Added `validate_eof` EVMC option to validate EOF before execution. + This option is enabled by default in `evmc run`. + [#768](https://github.com/ethereum/evmone/pull/768) + [#960](https://github.com/ethereum/evmone/pull/960) +- Implemented [EIP-7610] "Revert creation in case of non-empty storage" + in the testing infrastructure. + [#816](https://github.com/ethereum/evmone/pull/816) +- Added `--version` option to testing tools. + [#902](https://github.com/ethereum/evmone/pull/902) +- Introduce `TestState` and `TestAccount` to testing infrastructure. + [#811](https://github.com/ethereum/evmone/pull/811) +- Added support for validating "initcode" containers in `eofparse` and `eoftest`. + [#934](https://github.com/ethereum/evmone/pull/934) + [#943](https://github.com/ethereum/evmone/pull/943) + +### Changed + +- **EVM Object Format (EOF)** + + Completed implementation of the [EIP-7692]: EVM Object Format (EOFv1) Meta. + - Added `EOFCREATE` and `RETURNCONTRACT` instructions. + [#553](https://github.com/ethereum/evmone/pull/553) + - Added `TXCREATE` instruction, later moved to the future EOF version (Osaka). + [#702](https://github.com/ethereum/evmone/pull/702) + [#889](https://github.com/ethereum/evmone/pull/889) + - Make `EXT*CALL` instructions Address Space Expansion ready. + [#915](https://github.com/ethereum/evmone/pull/915) + - Added EOF validation of sub-container kinds. + [#876](https://github.com/ethereum/evmone/pull/876) + - Limit validated container size to `MAX_INITCODE_SIZE`. + [#930](https://github.com/ethereum/evmone/pull/930) + - Added `RETURNDATALOAD` instruction. + [#786](https://github.com/ethereum/evmone/pull/786) + - Implementation of "less restricted" stack validation. + [#676](https://github.com/ethereum/evmone/pull/676) + - Added implementation of `EXCHANGE` from [EIP-663]. + [#839](https://github.com/ethereum/evmone/pull/839) + - Disallow unreachable code sections in EOF containers. + [#721](https://github.com/ethereum/evmone/pull/721) + [#866](https://github.com/ethereum/evmone/pull/866) + - Restrict `DUPN` and `SWAPN` to EOF only in EOF only. + [#788](https://github.com/ethereum/evmone/pull/788) + - Change `DATA*` opcodes. + [#797](https://github.com/ethereum/evmone/pull/797) + - Disable EOF ↔ legacy cross-creation. + [#825](https://github.com/ethereum/evmone/pull/825) + - Deprecate and reject code/gas-observability in EOF. + [#834](https://github.com/ethereum/evmone/pull/834) + - Make EOF opaque for `EXTCODE*` instructions. + [#587](https://github.com/ethereum/evmone/pull/587) + - Implement EOF creation transactions. + [#878](https://github.com/ethereum/evmone/pull/878) + - Modify EOF `RETURNDATA*` to allow out-of-bounds reads (instead of failing execution). + [#909](https://github.com/ethereum/evmone/pull/909) + - Tune EOF validation: disallow truncated data in top-level EOF containers. + [#921](https://github.com/ethereum/evmone/pull/921) + - Disallow unreferenced sub-containers and sub-containers of conflicting kinds. + [#916](https://github.com/ethereum/evmone/pull/916) + +- **Testing** + + There are a lot of improvements to the testing tools and test formats. + In particular, big portion of evmone's unit tests has been re-shaped + to have a structure of State Tests or EOF Validation Tests. + + Moreover, we added the option to export these tests to JSON and the archive + of the exported tests ("fixture") is the artifact of this release. + + Upgraded external test suites: + - [ethereum/tests][Ethereum Execution Tests]: [14.0][tests 14.0] + - [Execution Spec Tests]: [3.0.0][Execution Spec Tests 3.0.0] + + Other details: + - Add some missing State Test export features. + [#807](https://github.com/ethereum/evmone/pull/807) + - Check for unexpected `EF` prefix in test code. + [#809](https://github.com/ethereum/evmone/pull/809) + - EOF Validation Test fixture. + [#810](https://github.com/ethereum/evmone/pull/810) + - Export EOF validation unit tests to JSON EOF Validation Tests. + [#818](https://github.com/ethereum/evmone/pull/818) + - Output failed test case index in EOF Validation Tests. + [#820](https://github.com/ethereum/evmone/pull/820) + - Add `ExportableFixture` for JSON tests exporting. + [#821](https://github.com/ethereum/evmone/pull/821) + - Recognize all official fork/revision names. + [#830](https://github.com/ethereum/evmone/pull/830) + - Export State Tests with invalid transactions. + [#858](https://github.com/ethereum/evmone/pull/858) + - Allow `"to": null` in JSON transactions. + [#927](https://github.com/ethereum/evmone/pull/927) + - EOF Validation Tests runner: support "initcode" flag. + [#936](https://github.com/ethereum/evmone/pull/936) + - `evmone-blockchaintest`: Simplify genesis handling. + [#954](https://github.com/ethereum/evmone/pull/954) + - Optimization: only empty accounts are marked "touched". + [#785](https://github.com/ethereum/evmone/pull/785) + - Adjust ethash difficulty if below minimum `0x20000`. + [#803](https://github.com/ethereum/evmone/pull/803) + +- Requirements and dependencies updates: + - CMake 3.18 + [#840](https://github.com/ethereum/evmone/pull/840) + - Xcode 15.0 + [#847](https://github.com/ethereum/evmone/pull/847) + - [EVMC] [12.0.0][EVMC 12.0.0] + [#966](https://github.com/ethereum/evmone/pull/966) + - [intx] [0.11.0][intx 0.11.0] + [#967](https://github.com/ethereum/evmone/pull/967) + +- Use 32-byte aligned allocation for Baseline stack space. + [#907](https://github.com/ethereum/evmone/pull/907) +- Split Baseline analysis and execution into separate files. + [#946](https://github.com/ethereum/evmone/pull/946) +- Convert EVMMAX to header-only library with full `constexpr` capabilities. + [#864](https://github.com/ethereum/evmone/pull/864) + [#964](https://github.com/ethereum/evmone/pull/964) +- Return number of errors from `eofparse`. + [#873](https://github.com/ethereum/evmone/pull/873) + +### Fixed + +- Implement Frontier behavior of failing code deployment (testing infrastructure). + [#824](https://github.com/ethereum/evmone/pull/824) +- Fix error messages for compatibility with external testing tools. + [#828](https://github.com/ethereum/evmone/pull/828) + [#886](https://github.com/ethereum/evmone/pull/886) +- Fix initcode handling before EOF is enabled: + an initcode staring with `EF00` should not be validated as EOF unconditionally. + [#893](https://github.com/ethereum/evmone/pull/893) +- Fix EOF header parsing bug (introduced by code refactoring). + [#957](https://github.com/ethereum/evmone/pull/957) + [#958](https://github.com/ethereum/evmone/pull/958) +- Fix `eoftest` to run all tests from a JSON file. + [#935](https://github.com/ethereum/evmone/pull/935) +- Improve output buffer handling for precompiles in testing infrastructure. + This fixes out-of-bound access for some fuzzing-generated state tests. + [#951](https://github.com/ethereum/evmone/pull/951) + + +## [0.11.0] — 2023-12-21 + +This release is focused on [Cancun] and EOF. + +### Added + +- **[Cancun] Network Upgrade fully supported** + - [EIP-1153]: Transient storage opcodes + - transient storage & `TLOAD` and `TSTORE` instructions + [#669](https://github.com/ethereum/evmone/pull/669) + - clearing of transient storage between transactions + [#715](https://github.com/ethereum/evmone/pull/715) + - [EIP-4788]: Beacon block root in the EVM + - don't assume the transaction sender exists + [#731](https://github.com/ethereum/evmone/pull/731) + - system call to the _Beacon Roots_ contract + [#709](https://github.com/ethereum/evmone/pull/709) + - [EIP-4844]: Shard Blob Transactions + - `BLOBHASH` instruction + [#668](https://github.com/ethereum/evmone/pull/668) + - blob transactions + [#713](https://github.com/ethereum/evmone/pull/713) + - stub of the `point_evaluation` precompile + [#730](https://github.com/ethereum/evmone/pull/730) + - [EIP-5656]: `MCOPY` - Memory copying instruction + [#629](https://github.com/ethereum/evmone/pull/629) + [#648](https://github.com/ethereum/evmone/pull/648) + - [EIP-6780]: `SELFDESTRUCT` only in same transaction + [#735](https://github.com/ethereum/evmone/pull/735) + - [EIP-7516]: `BLOBBASEFEE` opcode + [#708](https://github.com/ethereum/evmone/pull/708) +- **EVM Modular Arithmetic Extensions ([EVMMAX])** + - Added basic EVMMAX support in form of C++ API. + [#673](https://github.com/ethereum/evmone/pull/673) + - Implementation of secp256k1 ECDSA recovery (`ecrecovery` precompile) using EVMMAX + [#688](https://github.com/ethereum/evmone/pull/688) + - Implementation of `ecadd` and `ecmul` BN254 precompiles using EVMMAX + [#716](https://github.com/ethereum/evmone/pull/716) +- Initial support for [Blockchain Tests] + - block execution + [#681](https://github.com/ethereum/evmone/pull/681) + [#679](https://github.com/ethereum/evmone/pull/679) + [#685](https://github.com/ethereum/evmone/pull/685) + [#701](https://github.com/ethereum/evmone/pull/701) + - test format support & results encoding + [#680](https://github.com/ethereum/evmone/pull/680) + [#690](https://github.com/ethereum/evmone/pull/690) + [#694](https://github.com/ethereum/evmone/pull/694) + [#711](https://github.com/ethereum/evmone/pull/711) + [#736](https://github.com/ethereum/evmone/pull/736) + - PoW difficulty calculation + [#682](https://github.com/ethereum/evmone/pull/682) + [#718](https://github.com/ethereum/evmone/pull/718) +- Optionally use [Silkworm] as the precompiles implementation. + [#660](https://github.com/ethereum/evmone/pull/660) +- Support for executing [JSON EOF Tests](https://github.com/ethereum/tests/tree/v13/EOFTests) + (thanks @gzanitti) + [#678](https://github.com/ethereum/evmone/pull/678) +- EVM tracing option `--trace` in `evmone-t8n` + [#616](https://github.com/ethereum/evmone/pull/616) +- Support for compiling for `riscv32` architecture + [#700](https://github.com/ethereum/evmone/pull/700) +- Ability to export evmone's unit tests to the JSON State Test format + [#743](https://github.com/ethereum/evmone/pull/743) + +### Changed + +- **EVM Object Format (EOF)** + + EOF implementation follows the [EOF spec] (aka _Mega EOF Endgame_) + and is tentatively enabled in the **Prague** EVM revision. + - Tests have been migrated to [ipsilon/tests/eof](https://github.com/ipsilon/tests/tree/eof) + [#651](https://github.com/ethereum/evmone/pull/651) + - Implementation of four new instructions for accessing _data sections_: + `DATALOAD`, `DATALOADN`, `DATASIZE`, `DATACOPY` + [#586](https://github.com/ethereum/evmone/pull/586) + [#663](https://github.com/ethereum/evmone/pull/663) + [#717](https://github.com/ethereum/evmone/pull/717) + [#741](https://github.com/ethereum/evmone/pull/741) + - Forbid `DELEGATECALL` from EOF to legacy contracts during execution + [#588](https://github.com/ethereum/evmone/pull/588) + - The data section kind has been changed to `0x04` + [#632](https://github.com/ethereum/evmone/pull/632) + - The `RJUMPV` immediate argument meaning has been changed to "max index" + [#640](https://github.com/ethereum/evmone/pull/640) + - Implementation of the `JUMPF` instruction and the _non-returning_ functions + [#644](https://github.com/ethereum/evmone/pull/644) + +- Opcodes of new instructions have been assigned following + [the execution-spec opcodes list](https://github.com/ethereum/execution-specs/tree/master/lists/evm) + [#665](https://github.com/ethereum/evmone/pull/665) +- State changes are now reverted with the journal + [#689](https://github.com/ethereum/evmone/pull/689) +- Compatibility of `evmone-statetest` with [goevmlab] has been improved + [#658](https://github.com/ethereum/evmone/pull/658) + [#757](https://github.com/ethereum/evmone/pull/757) +- Minimal tested/supported compilers versions: + [#675](https://github.com/ethereum/evmone/pull/675) + - GCC 11 + - Clang 13 + - XCode 14.3.1 (bumped from 13.4) + - Visual Studio 2022 + - CMake 3.16...3.27 +- [EVMC] has been upgraded to version [11.0.1][EVMC 11.0.1]. + [#754](https://github.com/ethereum/evmone/pull/754) + [#738](https://github.com/ethereum/evmone/pull/738) + [#707](https://github.com/ethereum/evmone/pull/707) + [#669](https://github.com/ethereum/evmone/pull/669) +- [intx] has been upgraded to version [0.10.1][intx 0.10.1]. + [#674](https://github.com/ethereum/evmone/pull/674) +- [Ethereum Execution Tests] has been upgraded to version [13][tests 13] + and [Execution Spec Tests] version [1.0.6][Execution Spec Tests 1.0.6] has been added. + [#737](https://github.com/ethereum/evmone/pull/737) + +### Fixed + +- EOF: Fix `CALLF` runtime stack overflow check + [#677](https://github.com/ethereum/evmone/pull/677) +- EOF: Fix missing `CALLF` stack overflow validation + [#619](https://github.com/ethereum/evmone/pull/619) +- Fixed processing of withdrawals with 0 amount (testing infrastructure) + [#630](https://github.com/ethereum/evmone/pull/630) +- Fixed handling of _short_ nodes in Merkle Patricia Trie (testing infrastructure) + [#686](https://github.com/ethereum/evmone/pull/686) + + ## [0.10.0] — 2023-05-08 The highlights of this release are support for [Shanghai] execution specification upgrade @@ -48,7 +400,8 @@ the Baseline interpreter is now: [#609](https://github.com/ethereum/evmone/pull/609) - Added **[t8n]** tool `evmone-t8n` — a command line utility for transaction execution and state transition testing. - It allows executing and generating tests with cooperation of [retesteth] or [execution-spec-tests]. + It allows executing and generating tests with cooperation of [retesteth] + or [Execution Spec Tests]. [#552](https://github.com/ethereum/evmone/pull/552) [#555](https://github.com/ethereum/evmone/pull/555) [#558](https://github.com/ethereum/evmone/pull/558) @@ -488,7 +841,9 @@ It delivers fully-compatible and high-speed EVM implementation. - Exposes [EVMC] 6 ABI. - The [intx 0.2.0](https://github.com/chfast/intx/releases/tag/v0.2.0) library is used for 256-bit precision arithmetic. - +[0.13.0]: https://github.com/ethereum/evmone/releases/tag/v0.13.0 +[0.12.0]: https://github.com/ethereum/evmone/releases/tag/v0.12.0 +[0.11.0]: https://github.com/ethereum/evmone/releases/tag/v0.11.0 [0.10.0]: https://github.com/ethereum/evmone/releases/tag/v0.10.0 [0.9.1]: https://github.com/ethereum/evmone/releases/tag/v0.9.1 [0.9.0]: https://github.com/ethereum/evmone/releases/tag/v0.9.0 @@ -505,13 +860,15 @@ It delivers fully-compatible and high-speed EVM implementation. [0.1.1]: https://github.com/ethereum/evmone/releases/tag/v0.1.1 [0.1.0]: https://github.com/ethereum/evmone/releases/tag/v0.1.0 -[Aleth]: https://github.com/ethereum/aleth [EIP-170]: https://eips.ethereum.org/EIPS/eip-170 [EIP-663]: https://eips.ethereum.org/EIPS/eip-663 +[EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153 [EIP-1884]: https://eips.ethereum.org/EIPS/eip-1884 [EIP-1344]: https://eips.ethereum.org/EIPS/eip-1344 [EIP-2200]: https://eips.ethereum.org/EIPS/eip-2200 +[EIP-2537]: https://eips.ethereum.org/EIPS/eip-2537 [EIP-2929]: https://eips.ethereum.org/EIPS/eip-2929 +[EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 [EIP-3155]: https://eips.ethereum.org/EIPS/eip-3155 [EIP-3198]: https://eips.ethereum.org/EIPS/eip-3198 [EIP-3540]: https://eips.ethereum.org/EIPS/eip-3540 @@ -521,8 +878,16 @@ It delivers fully-compatible and high-speed EVM implementation. [EIP-3860]: https://eips.ethereum.org/EIPS/eip-3860 [EIP-4200]: https://eips.ethereum.org/EIPS/eip-4200 [EIP-4750]: https://eips.ethereum.org/EIPS/eip-4750 +[EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +[EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 [EIP-4895]: https://eips.ethereum.org/EIPS/eip-4895 [EIP-5450]: https://eips.ethereum.org/EIPS/eip-5450 +[EIP-5656]: https://eips.ethereum.org/EIPS/eip-5656 +[EIP-6780]: https://eips.ethereum.org/EIPS/eip-6780 +[EIP-7516]: https://eips.ethereum.org/EIPS/eip-7516 +[EIP-7610]: https://eips.ethereum.org/EIPS/eip-7610 +[EIP-7692]: https://eips.ethereum.org/EIPS/eip-7692 + [Spurious Dragon]: https://eips.ethereum.org/EIPS/eip-607 [Petersburg]: https://eips.ethereum.org/EIPS/eip-1716 [Istanbul]: https://eips.ethereum.org/EIPS/eip-1679 @@ -530,8 +895,11 @@ It delivers fully-compatible and high-speed EVM implementation. [London]: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md [Shanghai]: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md [Cancun]: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md -[EOF]: https://notes.ethereum.org/@ipsilon/evm-object-format-overview +[Prague]: https://eips.ethereum.org/EIPS/eip-7600 + [EVMC]: https://github.com/ethereum/evmc +[EVMC 12.0.0]: https://github.com/ethereum/evmc/releases/tag/v12.0.0 +[EVMC 11.0.1]: https://github.com/ethereum/evmc/releases/tag/v11.0.1 [EVMC 10.1.0]: https://github.com/ethereum/evmc/releases/tag/v10.1.0 [EVMC 10.0.0]: https://github.com/ethereum/evmc/releases/tag/v10.0.0 [EVMC 9.0.0]: https://github.com/ethereum/evmc/releases/tag/v9.0.0 @@ -540,22 +908,42 @@ It delivers fully-compatible and high-speed EVM implementation. [EVMC 7.4.0]: https://github.com/ethereum/evmc/releases/tag/v7.4.0 [EVMC 7.1.0]: https://github.com/ethereum/evmc/releases/tag/v7.1.0 [EVMC 7.0.0]: https://github.com/ethereum/evmc/releases/tag/v7.0.0 + [intx]: https://github.com/chfast/intx +[intx 0.11.0]: https://github.com/chfast/intx/releases/tag/v0.11.0 +[intx 0.10.1]: https://github.com/chfast/intx/releases/tag/v0.10.1 [intx 0.10.0]: https://github.com/chfast/intx/releases/tag/v0.10.0 [intx 0.8.0]: https://github.com/chfast/intx/releases/tag/v0.8.0 [intx 0.6.0]: https://github.com/chfast/intx/releases/tag/v0.6.0 [intx 0.5.0]: https://github.com/chfast/intx/releases/tag/v0.5.0 + [ethash]: https://github.com/chfast/ethash [ethash 0.7.0]: https://github.com/chfast/ethash/releases/tag/v0.7.0 [ethash 1.0.0]: https://github.com/chfast/ethash/releases/tag/v1.0.0 + +[ethereum/tests]: https://github.com/ethereum/tests [Ethereum Execution Tests]: https://github.com/ethereum/tests +[tests 14.0]: https://github.com/ethereum/tests/releases/tag/v14.0 +[tests 13]: https://github.com/ethereum/tests/releases/tag/v13 [tests 12.2]: https://github.com/ethereum/tests/releases/tag/v12.2 [tests 9.0.2]: https://github.com/ethereum/tests/releases/tag/9.0.2 [tests 8.0.4]: https://github.com/ethereum/tests/releases/tag/8.0.4 + +[Execution Spec Tests]: https://github.com/ethereum/execution-spec-tests +[Execution Spec Tests 3.0.0]: https://github.com/ethereum/execution-spec-tests/releases/tag/v3.0.0 +[Execution Spec Tests 1.0.6]: https://github.com/ethereum/execution-spec-tests/releases/tag/v1.0.6 + +[Aleth]: https://github.com/ethereum/aleth +[Blockchain Tests]: https://ethereum-tests.readthedocs.io/en/latest/blockchain-ref.html [evm-benchmarks]: https://github.com/ipsilon/evm-benchmarks -[execution-spec-tests]: https://github.com/ethereum/execution-spec-tests +[EVMMAX]: https://github.com/ethereum/EIPs/pull/6601 +[EOF]: https://notes.ethereum.org/@ipsilon/evm-object-format-overview +[EOF spec]: https://github.com/ipsilon/eof/blob/main/spec/eof.md +[goevmlab]: https://github.com/holiman/goevmlab [retesteth]: https://github.com/ethereum/retesteth [Silkworm]: https://github.com/torquem-ch/silkworm [t8n]: https://ethereum-tests.readthedocs.io/en/develop/t8ntool-ref.html +[blst]: https://github.com/supranational/blst + [Keep a Changelog]: https://keepachangelog.com/en/1.1.0/ [Semantic Versioning]: https://semver.org/spec/v2.0.0.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 817035d2..36fcf461 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # Copyright 2019 The evmone Authors. # SPDX-License-Identifier: Apache-2.0 -cmake_minimum_required(VERSION 3.16...3.24) +cmake_minimum_required(VERSION 3.18...3.27) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/evmc/.git) message(FATAL_ERROR "Git submodules not initialized, execute:\n git submodule update --init") @@ -25,7 +25,7 @@ cable_set_build_type(DEFAULT Release CONFIGURATION_TYPES Release Debug) include(Hunter/init) project(evmone LANGUAGES CXX C) -set(PROJECT_VERSION 0.10.0) +set(PROJECT_VERSION 0.13.0) string(REGEX MATCH "([0-9]+)\\.([0-9]+)" _ ${PROJECT_VERSION}) set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1}) @@ -41,22 +41,33 @@ find_package(ethash CONFIG REQUIRED) option(EVMC_TOOLS "Build EVMC test tools" ${EVMONE_TESTING}) option(EVMC_INSTALL "Install EVMC" OFF) -add_subdirectory(evmc) + +if(COMMAND block) # TODO(cmake-3.25) + block() + set(CMAKE_C_CLANG_TIDY "") + set(CMAKE_CXX_CLANG_TIDY "") + add_subdirectory(evmc) + endblock() +else() + add_subdirectory(evmc) +endif() cable_configure_compiler(NO_STACK_PROTECTION) if(CABLE_COMPILER_GNULIKE) add_compile_options( -Wmissing-declarations - -Wno-attributes # Allow using unknown attributes. $<$:-Wextra-semi> + $<$:-Wno-missing-field-initializers> + + $<$:-Wno-attributes> + $<$:-Wduplicated-cond> + $<$:-Wlogical-op> + + $<$:-Wno-unknown-attributes> + $<$:-Wduplicate-enum> + $<$:-Wnewline-eof> + $<$:-Wunreachable-code-aggressive> ) - cable_add_cxx_compiler_flag_if_supported(-Wduplicated-cond) - cable_add_cxx_compiler_flag_if_supported(-Wduplicate-enum) - cable_add_cxx_compiler_flag_if_supported(-Wfinal-dtor-non-final-class) - cable_add_cxx_compiler_flag_if_supported(-Wlogical-op) - cable_add_cxx_compiler_flag_if_supported(-Wnewline-eof) - cable_add_cxx_compiler_flag_if_supported(-Wsuggest-destructor-override) - cable_add_cxx_compiler_flag_if_supported(-Wunreachable-code-break) if(CABLE_COMPILER_CLANG) set(CMAKE_CXX_FLAGS_COVERAGE "-fprofile-instr-generate -fcoverage-mapping") diff --git a/README.md b/README.md index 84bf191d..e24c2d19 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,18 @@ To build the evmone EVMC module (shared library), test, and benchmark: build/bin/evmone-bench test/evm-benchmarks/benchmarks ``` +### Precompiles + +Ethereum Precompiled Contracts (_precompiles_ for short) are only partly supported by evmone. + +However, there are options to enable limited precompiles support for testing. + +1. For precompiles with missing implementation stubs are enabled by default. + They will correctly respond to known inputs. +2. The CMake option `EVMONE_PRECOMPILES_SILKPRE=1` enables building of + the [silkpre] third party library with the implementation of the precompiles. + This library also requires [GMP] (e.g. libgmp-dev) library for building and execution. + ### Tools #### evm-test @@ -114,6 +126,14 @@ with it. docker run --entrypoint evmone-bench ethereum/evmone /src/test/benchmarks ``` +### EVM Object Format (EOF) support + +evmone supports EOFv1. Since EOF validation is done once during deploy-time, evmone does not revalidate during execution of bytecode. To force EOF revalidation, you can use the `validate_eof` option, example: + +``` +evmc run --vm libevmone.so,validate_eof --rev 13 "EF00" +``` + ## References 1. [Efficient gas calculation algorithm for EVM](docs/efficient_gas_calculation_algorithm.md) @@ -138,10 +158,12 @@ Licensed under the [Apache License, Version 2.0]. [EVMC]: https://github.com/ethereum/evmc [Ipsilon]: https://github.com/ipsilon [Ewasm]: https://github.com/ewasm +[GMP]: https://gmplib.org [intx]: https://github.com/chfast/intx [ethash]: https://github.com/chfast/ethash [Releases]: https://github.com/ethereum/evmone/releases [standard readme]: https://github.com/RichardLitt/standard-readme +[silkpre]: https://github.com/torquem-ch/silkpre [appveyor badge]: https://img.shields.io/appveyor/ci/chfast/evmone/master.svg?logo=appveyor [circleci badge]: https://img.shields.io/circleci/project/github/ethereum/evmone/master.svg?logo=circleci diff --git a/circle.yml b/circle.yml index 67631341..668aaf92 100644 --- a/circle.yml +++ b/circle.yml @@ -1,37 +1,38 @@ version: 2.1 orbs: + codecov: codecov/codecov@4.1.0 win: circleci/windows@5.0 executors: lint: docker: - - image: ethereum/cpp-build-env:19-lint + - image: ethereum/cpp-build-env:22-lint resource_class: small environment: CMAKE_BUILD_PARALLEL_LEVEL: 2 linux-gcc-latest: docker: - - image: ethereum/cpp-build-env:19-gcc-12 + - image: ethereum/cpp-build-env:22-gcc-14 environment: CMAKE_BUILD_PARALLEL_LEVEL: 4 linux-gcc-multilib: docker: - - image: ethereum/cpp-build-env:19-gcc-12-multilib + - image: ethereum/cpp-build-env:22-gcc-14-multilib resource_class: small environment: CMAKE_BUILD_PARALLEL_LEVEL: 2 - blockchain-tests: + linux-clang-xlarge: docker: - - image: ethereum/cpp-build-env:19-gcc-12 + - image: ethereum/cpp-build-env:22-clang-18 resource_class: xlarge environment: CMAKE_BUILD_PARALLEL_LEVEL: 8 - linux-clang-xlarge: + linux-clang-2xlarge: docker: - - image: ethereum/cpp-build-env:19-clang-15 - resource_class: xlarge + - image: ethereum/cpp-build-env:21-clang-17 + resource_class: 2xlarge environment: - CMAKE_BUILD_PARALLEL_LEVEL: 8 + CMAKE_BUILD_PARALLEL_LEVEL: 16 linux-gcc-min: docker: - image: ethereum/cpp-build-env:17-gcc-11 @@ -40,12 +41,12 @@ executors: CMAKE_BUILD_PARALLEL_LEVEL: 2 linux-clang-latest: docker: - - image: ethereum/cpp-build-env:19-clang-15 + - image: ethereum/cpp-build-env:22-clang-18 environment: CMAKE_BUILD_PARALLEL_LEVEL: 4 linux-clang-min: docker: - - image: ethereum/cpp-build-env:18-clang-13 + - image: ethereum/cpp-build-env:19-clang-15 resource_class: small environment: CMAKE_BUILD_PARALLEL_LEVEL: 2 @@ -56,15 +57,15 @@ executors: environment: CMAKE_BUILD_PARALLEL_LEVEL: 2 macos: - resource_class: macos.x86.medium.gen2 + resource_class: macos.m1.large.gen1 macos: - xcode: 14.3.0 + xcode: 15.4.0 environment: - CMAKE_BUILD_PARALLEL_LEVEL: 4 + CMAKE_BUILD_PARALLEL_LEVEL: 8 macos-xcode-min: - resource_class: macos.x86.medium.gen2 + resource_class: macos.m1.medium.gen1 macos: - xcode: 13.4.1 + xcode: 15.0.0 environment: CMAKE_BUILD_PARALLEL_LEVEL: 4 @@ -80,82 +81,69 @@ commands: command: | curl -L https://github.com/Kitware/CMake/releases/download/v<>/cmake-<>-linux-x86_64.tar.gz | sudo tar -xz --strip=1 - checkout_submodules: + install_riscv_toolchain: steps: - run: - name: "Update submodules" - command: git submodule update --init --recursive + name: "Install RISC-V Toolchain" + working_directory: /usr/local + command: | + curl -L https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.07/riscv32-glibc-ubuntu-22.04-llvm-nightly-2023.07.07-nightly.tar.gz | sudo tar -xz - build_silkworm: - parameters: - branch: - type: string - default: master - commit: - type: string + checkout_submodules: steps: - run: - # Fix fixes the cache restore step in case the silkworm dir does not exist - name: "Make Silkworm dir" - command: mkdir -p ~/silkworm - - restore_cache: - name: "Restore Silkworm cache (<>-<>)" - key: &silkworm-cache-key silkworm-v2-<>-<> - - run: - name: "Check Silkworm cache" - command: | - if [ -f ~/silkworm/consensus ]; then - echo 'Cached Silkworm binary available - skip build.' - else - echo 'export SILKWORM_BUILD=true' >> $BASH_ENV - fi - - run: - name: "Install build dependencies" - command: | - sudo apt-get -q update && sudo apt-get -qy install --no-install-recommends binutils m4 texinfo - - run: - name: "Checkout Silkworm" - working_directory: ~/silkworm/src - command: | - [ "$SILKWORM_BUILD" = true ] || exit 0 - git clone --no-checkout --single-branch https://github.com/torquem-ch/silkworm.git . --branch <> - git checkout <> - git submodule update --init --recursive --progress - - run: - name: "Configure Silkworm" - working_directory: ~/silkworm - command: | - [ "$SILKWORM_BUILD" = true ] || exit 0 - cmake -S src -B build -DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd) - - run: - name: "Build Silkworm cmd/consensus" - working_directory: ~/silkworm - command: | - [ "$SILKWORM_BUILD" = true ] || exit 0 - cmake --build build --target consensus - - save_cache: - name: "Save Silkworm cache" - key: *silkworm-cache-key - paths: - - ~/silkworm/consensus + name: "Update submodules" + command: git submodule update --init --recursive download_execution_tests: parameters: + repo: + type: string + default: "ethereum/tests" rev: type: string default: develop commit: type: string default: "" + legacy: + description: "Download also legacy tests" + type: boolean + default: true steps: - run: name: "Download execution tests: <> <>" working_directory: ~/tests command: | - git clone --no-checkout --depth=250 --single-branch https://github.com/ethereum/tests . --branch <> + find . -delete + git clone --no-checkout --depth=100 --single-branch https://github.com/<> . --branch <> <<#parameters.rev>>git checkout <><> <<#parameters.commit>>git checkout <><> - git submodule update --init --recursive --depth=1 --progress + - when: + condition: <> + steps: + - run: + name: "Download legacy execution tests (git submodule)" + working_directory: ~/tests + command: git submodule update --init --recursive --depth=1 --progress + + download_execution_spec_tests: + parameters: + repo: + type: string + default: ethereum/execution-spec-tests + release: + type: string + fixtures_suffix: + type: string + default: stable + steps: + - run: + name: "Download execution-spec-tests: <>" + working_directory: ~/spec-tests + command: | + curl -L https://github.com/<>/releases/download/<>/fixtures_<>.tar.gz | tar -xz + ls -l build: description: "Build" @@ -221,11 +209,10 @@ commands: - run: name: "Collect coverage data (GCC)" working_directory: ~/build + # Use coveralls to eliminate branch stats (they need more work) command: | - lcov --capture --directory . --output-file coverage.lcov --exclude='/usr/*' --exclude="$HOME/.hunter/*" --exclude="$PWD/_deps/*" - lcov --zerocounters --directory . - rm -rf ~/coverage - genhtml coverage.lcov --output-directory ~/coverage --title $CIRCLE_PROJECT_REPONAME + mkdir -p ~/coverage + gcovr --filter ~/project --coveralls coverage.json --html ~/coverage/coverage.html --html-nested ~/coverage/coverage.html - store_artifacts: path: ~/coverage destination: coverage @@ -236,29 +223,10 @@ commands: flags: type: string steps: - - run: - name: "Install codecov" - command: | - # TODO: This should go to cpp-build-env image - gpg --no-default-keyring --keyring trustedkeys.gpg --keyserver keyserver.ubuntu.com --recv-key ED779869 - - export CODECOV_VERSION=v0.4.1 - curl -Os https://uploader.codecov.io/$CODECOV_VERSION/linux/codecov - curl -Os https://uploader.codecov.io/$CODECOV_VERSION/linux/codecov.SHA256SUM - curl -Os https://uploader.codecov.io/$CODECOV_VERSION/linux/codecov.SHA256SUM.sig - - gpgv codecov.SHA256SUM.sig codecov.SHA256SUM - shasum -c codecov.SHA256SUM - - chmod +x codecov - sudo mv codecov /usr/local/bin - - - run: - name: "Upload to Codecov" - command: | - # Convert to relative paths - sed -i 's|$(pwd)/||' ~/build/coverage.lcov - codecov --flags <> --required --file ~/build/coverage.lcov -X gcov + - codecov/upload: + upload_args: --plugin noop + file: ../build/coverage.json + flags: <> package: description: "Make package" @@ -309,6 +277,10 @@ jobs: steps: - build - test + - run: + name: "Export JSON tests" + working_directory: ~/package + command: tar -C ~/build/test/integration/export -cz fixtures > evmone-tests.tar.gz - package release-windows: @@ -342,37 +314,6 @@ jobs: - test - package - release-windows-32bit: - executor: win/server-2022 - environment: - CMAKE_BUILD_TYPE: Release - CMAKE_BUILD_PARALLEL_LEVEL: 4 - steps: - - checkout - - checkout_submodules - - run: - name: "Setup environment (bash)" - shell: bash - command: | - echo 'export PATH=$PATH:"/c/Program Files/Microsoft Visual Studio/2022/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin"' >> $BASH_ENV - - run: - name: 'Configure' - shell: powershell - command: | - $ErrorActionPreference = "Stop" - & 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1' -Arch x86 - which cmake - cmake -S . -B ~/build -G Ninja -DCMAKE_INSTALL_PREFIX=C:\install -DEVMONE_TESTING=ON - - run: - name: 'Build' - shell: powershell - command: | - $ErrorActionPreference = "Stop" - & 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1' -Arch x86 - cmake --build ~/build - - test - - package - release-macos: executor: macos environment: @@ -404,49 +345,146 @@ jobs: echo $name ghr -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -n "$name" $prerelease_flag $CIRCLE_TAG ~/package - blockchain-tests: - executor: blockchain-tests + execution-spec-tests: + executor: linux-gcc-latest + environment: + BUILD_TYPE: Coverage + steps: + - build + - download_execution_spec_tests: + release: v3.0.0 + - run: + name: "Execution spec tests (state_tests)" + working_directory: ~/build + command: > + bin/evmone-statetest ~/spec-tests/fixtures/state_tests + - run: + name: "Execution spec tests (blockchain_tests)" + working_directory: ~/build + command: > + bin/evmone-blockchaintest ~/spec-tests/fixtures/blockchain_tests + - download_execution_spec_tests: + release: pectra-devnet-3@v1.5.0 + fixtures_suffix: pectra-devnet-3 + - run: + name: "Execution spec tests (develop, state_tests)" + # Tests for in-development EVM revision currently passing. + working_directory: ~/spec-tests/fixtures/state_tests + command: > + ~/build/bin/evmone-statetest + prague/eip2537_bls_12_381_precompiles + - run: + name: "Execution spec tests (develop, blockchain_tests)" + # Tests for in-development EVM revision currently passing. + working_directory: ~/spec-tests/fixtures/blockchain_tests + command: > + ~/build/bin/evmone-blockchaintest + prague/eip2935_historical_block_hashes_from_state + - collect_coverage_gcc + - upload_coverage: + flags: execution_spec_tests + + eof-execution-spec-tests: + executor: linux-gcc-latest + environment: + BUILD_TYPE: Coverage + steps: + - build + - download_execution_spec_tests: + release: eip7692@v1.1.0 + fixtures_suffix: eip7692 + - run: + name: "EOF pre-release execution spec tests (state_tests)" + working_directory: ~/build + command: > + bin/evmone-statetest ~/spec-tests/fixtures/state_tests + - run: + name: "EOF pre-release execution spec tests (blockchain_tests)" + working_directory: ~/build + command: > + bin/evmone-blockchaintest ~/spec-tests/fixtures/blockchain_tests + - run: + name: "EOF pre-release execution spec tests (eof_tests)" + working_directory: ~/build + command: > + bin/evmone-eoftest ~/spec-tests/fixtures/eof_tests + - collect_coverage_gcc + - upload_coverage: + flags: eof_execution_spec_tests + + ethereum-tests: + executor: linux-gcc-latest environment: BUILD_TYPE: Coverage - CMAKE_OPTIONS: -DEVMONE_TESTING=OFF -DCMAKE_CXX_FLAGS=-Og steps: - build - - build_silkworm: - commit: 30ca8b324fcffb574c23c09fcba6386e57a7a5af - download_execution_tests: - rev: v12.2 + rev: v14.1 - run: - name: "Silkworm-driven blockchain tests (Advanced)" + name: "State tests" working_directory: ~/build - no_output_timeout: 20m - command: ~/silkworm/consensus --evm lib/libevmone.so,advanced --tests ~/tests --threads $CMAKE_BUILD_PARALLEL_LEVEL + command: > + bin/evmone-statetest + ~/tests/GeneralStateTests + ~/tests/LegacyTests/Cancun/GeneralStateTests + ~/tests/LegacyTests/Constantinople/GeneralStateTests - run: - name: "Silkworm-driven blockchain tests (Baseline)" + name: "EOF State tests" working_directory: ~/build - no_output_timeout: 20m - command: ~/silkworm/consensus --evm lib/libevmone.so --tests ~/tests --threads $CMAKE_BUILD_PARALLEL_LEVEL + command: | + bin/evmone-statetest ~/tests/EIPTests/StateTests/stEOF + - run: + name: "EOF validation tests" + working_directory: ~/build + command: > + bin/evmone-eoftest ~/tests/EOFTests + - run: + name: "Blockchain tests (GeneralStateTests)" + working_directory: ~/build + command: > + bin/evmone-blockchaintest + --gtest_filter='*:-VMTests/vmPerformance.*:*.*Call50000_sha256:*.CALLBlake2f_MaxRounds' + ~/tests/BlockchainTests/GeneralStateTests + - run: + name: "Blockchain tests (ValidBlocks)" + working_directory: ~/build + command: > + bin/evmone-blockchaintest + --gtest_filter='*:-bcMultiChainTest.*:bcTotalDifficultyTest.*:bcForkStressTest.ForkStressTest:bcGasPricerTest.RPC_API_Test:bcValidBlockTest.SimpleTx3LowS' + ~/tests/BlockchainTests/ValidBlocks + ~/tests/LegacyTests/Cancun/BlockchainTests/ValidBlocks + - run: + name: "Blockchain tests (EIPs)" + working_directory: ~/build + command: > + bin/evmone-blockchaintest + --gtest_filter='-*StateTests/stEOF/*.*:*StateTests/stEIP2537.*' + ~/tests/EIPTests/BlockchainTests/ - collect_coverage_gcc - upload_coverage: - flags: blockchaintests + flags: ethereum_tests - state-tests: - executor: blockchain-tests + precompiles-silkpre: + executor: linux-gcc-latest environment: BUILD_TYPE: Coverage - CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-Og + CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-Og -DEVMONE_PRECOMPILES_SILKPRE=1 steps: + - run: + name: "Install GMP" + command: sudo apt-get -q update && sudo apt-get -qy install libgmp-dev - build - download_execution_tests: - rev: v12.2 + rev: v14.1 + legacy: false - run: name: "State tests" working_directory: ~/build command: | - export EVMONE_PRECOMPILES_STUB=~/project/test/state/precompiles_stub.json - bin/evmone-statetest ~/tests/GeneralStateTests ~/tests/EIPTests/StateTests/stEOF ~/tests/LegacyTests/Constantinople/GeneralStateTests + bin/evmone-statetest ~/tests/GeneralStateTests - collect_coverage_gcc - upload_coverage: - flags: statetests + flags: ethereum_tests_silkpre gcc-min: executor: linux-gcc-min @@ -456,6 +494,8 @@ jobs: clang-min: executor: linux-clang-min + environment: + CMAKE_OPTIONS=-DEVMONE_X86_64_ARCH_LEVEL=3 steps: - build - test @@ -481,9 +521,6 @@ jobs: steps: - build - test - - run: - name: "Install valgrind" - command: sudo apt-get -q update && sudo apt-get -qy install --no-install-recommends valgrind - run: name: "memcheck" working_directory: ~/build @@ -500,12 +537,20 @@ jobs: clang-latest-sanitizers: executor: linux-clang-xlarge environment: - CMAKE_OPTIONS: -DBUILD_SHARED_LIBS=NO -DSANITIZE=address,undefined,shift-exponent,implicit-conversion,nullability -DCMAKE_CXX_CLANG_TIDY=clang-tidy + CMAKE_OPTIONS: -DBUILD_SHARED_LIBS=NO -DSANITIZE=address,undefined,shift-exponent,implicit-conversion,nullability UBSAN_OPTIONS: halt_on_error=1 steps: - build - test + clang-tidy: + executor: linux-clang-2xlarge + environment: + CMAKE_OPTIONS: -DCMAKE_CXX_CLANG_TIDY=clang-tidy -DCMAKE_CXX_FLAGS=-g0 + BUILD_TYPE: Debug + steps: + - build + clang-latest-coverage: executor: linux-clang-latest resource_class: small @@ -524,7 +569,7 @@ jobs: SHOW_ARGS='-format=html -show-branches=count -show-regions -show-expansions' mkdir ~/coverage - llvm-profdata merge *.profraw -o evmone.profdata + llvm-profdata merge -sparse *.profraw -o evmone.profdata llvm-cov show $ARGS $SHOW_ARGS > ~/coverage/full.html llvm-cov show $ARGS $SHOW_ARGS -region-coverage-lt=100 > ~/coverage/missing.html @@ -558,7 +603,7 @@ jobs: executor: macos environment: BUILD_TYPE: RelWithDebInfo - CMAKE_OPTIONS: -DSANITIZE=address + CMAKE_OPTIONS: -DSANITIZE=address,undefined TESTS_FILTER: unittests steps: - run: @@ -580,7 +625,7 @@ jobs: executor: linux-base steps: - install_cmake: - version: 3.16.9 + version: 3.18.4 - build - test @@ -590,9 +635,6 @@ jobs: BUILD_TYPE: Release QEMU_CPU: core2duo # The lowest 64-bit CPU I could find, but qemu64 should be good too. steps: - - run: - name: "Install qemu" - command: sudo apt update -y && sudo apt install -y qemu-user-static - build - run: name: "Check evmone.so" @@ -603,6 +645,16 @@ jobs: working_directory: ~/build command: (! qemu-x86_64-static bin/evmone-unittests 2>&1) | grep "CPU does not support" + riscv32: + executor: linux-gcc-latest + environment: + BUILD_TYPE: Release + CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=~/project/cmake/toolchains/riscv32.cmake + steps: + - install_riscv_toolchain + - build + - test + workflows: @@ -610,6 +662,7 @@ workflows: evmone: jobs: - lint + - clang-tidy - release-linux: filters: tags: @@ -618,7 +671,6 @@ workflows: filters: tags: only: /.*/ - - release-windows-32bit - release-macos: filters: tags: @@ -633,8 +685,10 @@ workflows: ignore: /.*/ tags: only: /^v[0-9].*/ - - state-tests - - blockchain-tests + - execution-spec-tests + - eof-execution-spec-tests + - ethereum-tests + - precompiles-silkpre - cmake-min - gcc-min - clang-min @@ -646,4 +700,5 @@ workflows: - xcode-min - gcc-32bit - x86-64-v1 + - riscv32 - fuzzing diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index 308060f1..551f4f94 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -11,7 +11,7 @@ hunter_cmake_args( hunter_config( intx - VERSION 0.10.0 - URL https://github.com/chfast/intx/archive/v0.10.0.tar.gz - SHA1 3a6ebe0b1a36527b6ef291ee93a8e508371e5b77 + VERSION 0.12.0 + URL https://github.com/chfast/intx/archive/v0.12.0.tar.gz + SHA1 18a64e8e88c50d53325d906c9211daef905b97f4 ) diff --git a/cmake/Hunter/init.cmake b/cmake/Hunter/init.cmake index d130a40e..abdccdcc 100644 --- a/cmake/Hunter/init.cmake +++ b/cmake/Hunter/init.cmake @@ -3,11 +3,12 @@ # SPDX-License-Identifier: Apache-2.0 set(HUNTER_CONFIGURATION_TYPES Release CACHE STRING "Build type of Hunter packages") +set(HUNTER_USE_CACHE_SERVERS NO CACHE STRING "Download binaries from Hunter cache servers") include(HunterGate) HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.24.11.tar.gz" - SHA1 "e49fb20a135675e406e95fbe9a591a630ccbbeaf" + URL "https://github.com/cpp-pm/hunter/archive/v0.25.3.tar.gz" + SHA1 "0dfbc2cb5c4cf7e83533733bdfd2125ff96680cb" LOCAL ) diff --git a/cmake/blst.cmake b/cmake/blst.cmake new file mode 100644 index 00000000..2ccf3f04 --- /dev/null +++ b/cmake/blst.cmake @@ -0,0 +1,50 @@ +include_guard() +include(ExternalProject) + +if(MSVC) + set(BLST_BUILD_SCRIPT build.bat) +else() + # Build CC compiler invocation. + set(BLST_CC ${CMAKE_C_COMPILER}) + if(CMAKE_OSX_SYSROOT) + set(BLST_CC "${BLST_CC} ${CMAKE_C_SYSROOT_FLAG} ${CMAKE_OSX_SYSROOT}") + endif() + if(CMAKE_OSX_DEPLOYMENT_TARGET) + set(BLST_CC "${BLST_CC} ${CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + if(CMAKE_C_FLAGS) + # Pass CFLAGS as part of CC (e.g. to pass -m32). Using CFLAGS directly overwrites blst's default. + set(BLST_CC "${BLST_CC} ${CMAKE_C_FLAGS}") + endif() + + set(BLST_BUILD_SCRIPT ./build.sh CC='${BLST_CC}' AR='${CMAKE_AR}') +endif() + +ExternalProject_Add( + blst + EXCLUDE_FROM_ALL TRUE + PREFIX ${PROJECT_BINARY_DIR}/deps + URL https://github.com/supranational/blst/archive/refs/tags/v0.3.13.tar.gz + URL_HASH SHA256=89772cef338e93bc0348ae531462752906e8fa34738e38035308a7931dd2948f + DOWNLOAD_NO_PROGRESS TRUE + CONFIGURE_COMMAND "" + BUILD_COMMAND ${BLST_BUILD_SCRIPT} + BUILD_IN_SOURCE TRUE + BUILD_BYPRODUCTS "/${CMAKE_STATIC_LIBRARY_PREFIX}blst${CMAKE_STATIC_LIBRARY_SUFFIX}" + LOG_BUILD TRUE + LOG_OUTPUT_ON_FAILURE TRUE + INSTALL_COMMAND "" +) +ExternalProject_Get_Property(blst SOURCE_DIR) + +set(BLST_INCLUDE_DIR ${SOURCE_DIR}/bindings) +file(MAKE_DIRECTORY ${BLST_INCLUDE_DIR}) + +add_library(blst::blst STATIC IMPORTED GLOBAL) +add_dependencies(blst::blst blst) +set_target_properties( + blst::blst PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${BLST_INCLUDE_DIR} + IMPORTED_LOCATION ${SOURCE_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}blst${CMAKE_STATIC_LIBRARY_SUFFIX} +) + diff --git a/cmake/cable/HunterGate.cmake b/cmake/cable/HunterGate.cmake index e78d3e89..17c6d380 100644 --- a/cmake/cable/HunterGate.cmake +++ b/cmake/cable/HunterGate.cmake @@ -25,7 +25,7 @@ # This is a gate file to Hunter package manager. # Include this file using `include` command and add package you need, example: # -# cmake_minimum_required(VERSION 3.2) +# cmake_minimum_required(VERSION 3.5) # # include("cmake/HunterGate.cmake") # HunterGate( @@ -39,16 +39,16 @@ # hunter_add_package(Boo COMPONENTS Bar Baz) # # Projects: -# * https://github.com/hunter-packages/gate/ -# * https://github.com/ruslo/hunter +# * https://github.com/cpp-pm/gate/ +# * https://github.com/cpp-pm/hunter option(HUNTER_ENABLED "Enable Hunter package manager support" ON) if(HUNTER_ENABLED) - if(CMAKE_VERSION VERSION_LESS "3.2") + if(CMAKE_VERSION VERSION_LESS "3.5") message( FATAL_ERROR - "At least CMake version 3.2 required for Hunter dependency management." + "At least CMake version 3.5 required for Hunter dependency management." " Update CMake or set HUNTER_ENABLED to OFF." ) endif() @@ -59,8 +59,9 @@ include(CMakeParseArguments) # cmake_parse_arguments option(HUNTER_STATUS_PRINT "Print working status" ON) option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) +set(HUNTER_ROOT "" CACHE FILEPATH "Override the HUNTER_ROOT.") -set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors") +set(HUNTER_ERROR_PAGE "https://hunter.readthedocs.io/en/latest/reference/errors") function(hunter_gate_status_print) if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) @@ -133,10 +134,14 @@ function(hunter_gate_self root version sha1 result) string(SUBSTRING "${sha1}" 0 7 archive_id) - set( - hunter_self - "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" - ) + if(EXISTS "${root}/cmake/Hunter") + set(hunter_self "${root}") + else() + set( + hunter_self + "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" + ) + endif() set("${result}" "${hunter_self}" PARENT_SCOPE) endfunction() @@ -144,24 +149,21 @@ endfunction() # Set HUNTER_GATE_ROOT cmake variable to suitable value. function(hunter_gate_detect_root) # Check CMake variable - string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) - if(not_empty) + if(HUNTER_ROOT) set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") return() endif() # Check environment variable - string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) - if(not_empty) + if(DEFINED ENV{HUNTER_ROOT}) set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") return() endif() # Check HOME environment variable - string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) - if(result) + if(DEFINED ENV{HOME}) set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") return() @@ -169,8 +171,7 @@ function(hunter_gate_detect_root) # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) if(WIN32) - string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) - if(result) + if(DEFINED ENV{SYSTEMDRIVE}) set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using SYSTEMDRIVE environment variable" @@ -178,8 +179,7 @@ function(hunter_gate_detect_root) return() endif() - string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) - if(result) + if(DEFINED ENV{USERPROFILE}) set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using USERPROFILE environment variable" @@ -253,7 +253,13 @@ function(hunter_gate_download dir) file( WRITE "${cmakelists}" - "cmake_minimum_required(VERSION 3.2)\n" + "cmake_minimum_required(VERSION 3.5)\n" + "if(POLICY CMP0114)\n" + " cmake_policy(SET CMP0114 NEW)\n" + "endif()\n" + "if(POLICY CMP0135)\n" + " cmake_policy(SET CMP0135 NEW)\n" + "endif()\n" "project(HunterDownload LANGUAGES NONE)\n" "include(ExternalProject)\n" "ExternalProject_Add(\n" @@ -490,37 +496,46 @@ macro(HunterGate) ) set(_master_location "${_hunter_self}/cmake/Hunter") - get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) - set(_done_location "${_archive_id_location}/DONE") - set(_sha1_location "${_archive_id_location}/SHA1") - - # Check Hunter already downloaded by HunterGate - if(NOT EXISTS "${_done_location}") - hunter_gate_download("${_archive_id_location}") - endif() + if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") + # Hunter downloaded manually (e.g. by 'git clone') + set(_unused "xxxxxxxxxx") + set(HUNTER_GATE_SHA1 "${_unused}") + set(HUNTER_GATE_VERSION "${_unused}") + else() + get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) + set(_done_location "${_archive_id_location}/DONE") + set(_sha1_location "${_archive_id_location}/SHA1") + + # Check Hunter already downloaded by HunterGate + if(NOT EXISTS "${_done_location}") + hunter_gate_download("${_archive_id_location}") + endif() - if(NOT EXISTS "${_done_location}") - hunter_gate_internal_error("hunter_gate_download failed") - endif() + if(NOT EXISTS "${_done_location}") + hunter_gate_internal_error("hunter_gate_download failed") + endif() - if(NOT EXISTS "${_sha1_location}") - hunter_gate_internal_error("${_sha1_location} not found") - endif() - file(READ "${_sha1_location}" _sha1_value) - string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) - if(NOT _is_equal) - hunter_gate_internal_error( - "Short SHA1 collision:" - " ${_sha1_value} (from ${_sha1_location})" - " ${HUNTER_GATE_SHA1} (HunterGate)" - ) - endif() - if(NOT EXISTS "${_master_location}") - hunter_gate_user_error( - "Master file not found:" - " ${_master_location}" - "try to update Hunter/HunterGate" - ) + if(NOT EXISTS "${_sha1_location}") + hunter_gate_internal_error("${_sha1_location} not found") + endif() + file(READ "${_sha1_location}" _sha1_value) + string(TOLOWER "${_sha1_value}" _sha1_value_lower) + string(TOLOWER "${HUNTER_GATE_SHA1}" _HUNTER_GATE_SHA1_lower) + string(COMPARE EQUAL "${_sha1_value_lower}" "${_HUNTER_GATE_SHA1_lower}" _is_equal) + if(NOT _is_equal) + hunter_gate_internal_error( + "Short SHA1 collision:" + " ${_sha1_value} (from ${_sha1_location})" + " ${HUNTER_GATE_SHA1} (HunterGate)" + ) + endif() + if(NOT EXISTS "${_master_location}") + hunter_gate_user_error( + "Master file not found:" + " ${_master_location}" + "try to update Hunter/HunterGate" + ) + endif() endif() include("${_master_location}") set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) diff --git a/cmake/toolchains/riscv32.cmake b/cmake/toolchains/riscv32.cmake new file mode 100644 index 00000000..5fa0f754 --- /dev/null +++ b/cmake/toolchains/riscv32.cmake @@ -0,0 +1,19 @@ +# evmone: Ethereum Virtual Machine +# Copyright 2023 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(RISCV /usr/local/riscv) + +set(CMAKE_SYSTEM_PROCESSOR riscv32) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_C_COMPILER ${RISCV}/bin/clang) +set(CMAKE_CXX_COMPILER ${RISCV}/bin/clang++) + +set(CMAKE_CXX_FLAGS_INIT -stdlib=libc++) + +set(CMAKE_FIND_ROOT_PATH ${RISCV}/sysroot) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-riscv32-static;-L;${CMAKE_FIND_ROOT_PATH}) diff --git a/codecov.yml b/codecov.yml index ee83be13..b2cf7e26 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,13 +1,5 @@ +codecov: + require_ci_to_pass: false + comment: layout: "diff, flags, files" - -flags: - statetests: - paths: - - lib/evmone - - test/state - - test/statetest - blockchaintests: - joined: false - paths: - - lib/evmone diff --git a/evmc b/evmc index e8e70a55..42dd94d0 160000 --- a/evmc +++ b/evmc @@ -1 +1 @@ -Subproject commit e8e70a55ad90086de169221d1da7879028ee6a35 +Subproject commit 42dd94d0dc4761ef4a7362d2ee8b569dbd15818d diff --git a/include/evmmax/evmmax.hpp b/include/evmmax/evmmax.hpp new file mode 100644 index 00000000..70feec2b --- /dev/null +++ b/include/evmmax/evmmax.hpp @@ -0,0 +1,132 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmmax +{ + +/// The modular arithmetic operations for EVMMAX (EVM Modular Arithmetic Extensions). +template +class ModArith +{ +public: + const UintT mod; ///< The modulus. + +private: + const UintT m_r_squared; ///< R² % mod. + + /// The modulus inversion, i.e. the number N' such that mod⋅N' = 2⁶⁴-1. + const uint64_t m_mod_inv; + + /// Compute the modulus inverse for Montgomery multiplication, i.e. N': mod⋅N' = 2⁶⁴-1. + /// + /// @param mod0 The least significant word of the modulus. + static constexpr uint64_t compute_mod_inv(uint64_t mod0) noexcept + { + // TODO: Find what is this algorithm and why it works. + uint64_t base = 0 - mod0; + uint64_t result = 1; + for (auto i = 0; i < 64; ++i) + { + result *= base; + base *= base; + } + return result; + } + + /// Compute R² % mod. + static constexpr UintT compute_r_squared(const UintT& mod) noexcept + { + // R is 2^num_bits, R² is 2^(2*num_bits) and needs 2*num_bits+1 bits to represent, + // rounded to 2*num_bits+64) for intx requirements. + constexpr auto r2 = intx::uint{1} << (UintT::num_bits * 2); + return intx::udivrem(r2, mod).rem; + } + + static constexpr std::pair addmul( + uint64_t t, uint64_t a, uint64_t b, uint64_t c) noexcept + { + const auto p = intx::umul(a, b) + t + c; + return {p[1], p[0]}; + } + +public: + constexpr explicit ModArith(const UintT& modulus) noexcept + : mod{modulus}, + m_r_squared{compute_r_squared(modulus)}, + m_mod_inv{compute_mod_inv(modulus[0])} + {} + + /// Converts a value to Montgomery form. + /// + /// This is done by using Montgomery multiplication mul(x, R²) + /// what gives aR²R⁻¹ % mod = aR % mod. + constexpr UintT to_mont(const UintT& x) const noexcept { return mul(x, m_r_squared); } + + /// Converts a value in Montgomery form back to normal value. + /// + /// Given the x is the Montgomery form x = aR, the conversion is done by using + /// Montgomery multiplication mul(x, 1) what gives aRR⁻¹ % mod = a % mod. + constexpr UintT from_mont(const UintT& x) const noexcept { return mul(x, 1); } + + /// Performs a Montgomery modular multiplication. + /// + /// Inputs must be in Montgomery form: x = aR, y = bR. + /// This computes Montgomery multiplication xyR⁻¹ % mod what gives aRbRR⁻¹ % mod = abR % mod. + /// The result (abR) is in Montgomery form. + constexpr UintT mul(const UintT& x, const UintT& y) const noexcept + { + // Coarsely Integrated Operand Scanning (CIOS) Method + // Based on 2.3.2 from + // High-Speed Algorithms & Architectures For Number-Theoretic Cryptosystems + // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf + + constexpr auto S = UintT::num_words; // TODO(C++23): Make it static + + intx::uint t; + for (size_t i = 0; i != S; ++i) + { + uint64_t c = 0; + for (size_t j = 0; j != S; ++j) + std::tie(c, t[j]) = addmul(t[j], x[j], y[i], c); + auto tmp = intx::addc(t[S], c); + t[S] = tmp.value; + const auto d = tmp.carry; // TODO: Carry is 0 for sparse modulus. + + const auto m = t[0] * m_mod_inv; + std::tie(c, std::ignore) = addmul(t[0], m, mod[0], 0); + for (size_t j = 1; j != S; ++j) + std::tie(c, t[j - 1]) = addmul(t[j], m, mod[j], c); + tmp = intx::addc(t[S], c); + t[S - 1] = tmp.value; + t[S] = d + tmp.carry; // TODO: Carry is 0 for sparse modulus. + } + + if (t >= mod) + t -= mod; + + return static_cast(t); + } + + /// Performs a modular addition. It is required that x < mod and y < mod, but x and y may be + /// but are not required to be in Montgomery form. + constexpr UintT add(const UintT& x, const UintT& y) const noexcept + { + const auto s = addc(x, y); // TODO: cannot overflow if modulus is sparse (e.g. 255 bits). + const auto d = subc(s.value, mod); + return (!s.carry && d.carry) ? s.value : d.value; + } + + /// Performs a modular subtraction. It is required that x < mod and y < mod, but x and y may be + /// but are not required to be in Montgomery form. + constexpr UintT sub(const UintT& x, const UintT& y) const noexcept + { + const auto d = subc(x, y); + const auto s = d.value + mod; + return (d.carry) ? s : d.value; + } +}; +} // namespace evmmax diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b228843d..f2e75127 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,5 +1,10 @@ # evmone: Fast Ethereum Virtual Machine implementation -# Copyright 2018-2019 The evmone Authors. +# Copyright 2018 The evmone Authors. # SPDX-License-Identifier: Apache-2.0 +hunter_add_package(intx) +find_package(intx CONFIG REQUIRED) + +add_subdirectory(evmmax) add_subdirectory(evmone) +add_subdirectory(evmone_precompiles) diff --git a/lib/evmmax/CMakeLists.txt b/lib/evmmax/CMakeLists.txt new file mode 100644 index 00000000..ae4c6aa2 --- /dev/null +++ b/lib/evmmax/CMakeLists.txt @@ -0,0 +1,18 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +add_library(evmmax INTERFACE) +add_library(evmone::evmmax ALIAS evmmax) +target_compile_features(evmmax INTERFACE cxx_std_20) +target_include_directories(evmmax INTERFACE ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(evmmax INTERFACE intx::intx) + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) + # We want to add the header file to the library for IDEs. + # However, cmake 3.18 does not support PRIVATE scope for INTERFACE libraries. + target_sources( + evmmax PRIVATE + ${PROJECT_SOURCE_DIR}/include/evmmax/evmmax.hpp + ) +endif() diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index 28c403f2..777c0800 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -4,9 +4,6 @@ include(LibraryTools) -hunter_add_package(intx) -find_package(intx CONFIG REQUIRED) - add_library(evmone ${include_dir}/evmone/evmone.h advanced_analysis.cpp @@ -14,10 +11,12 @@ add_library(evmone advanced_execution.cpp advanced_execution.hpp advanced_instructions.cpp - baseline.cpp baseline.hpp + baseline_analysis.cpp + baseline_execution.cpp baseline_instruction_table.cpp baseline_instruction_table.hpp + constants.hpp eof.cpp eof.hpp instructions.hpp @@ -26,7 +25,6 @@ add_library(evmone instructions_storage.cpp instructions_traits.hpp instructions_xmacro.hpp - opcodes_helpers.h tracing.cpp tracing.hpp vm.cpp @@ -48,7 +46,7 @@ if(CABLE_COMPILER_GNULIKE) target_compile_options( evmone PRIVATE -fno-exceptions - $<$:-Wstack-usage=2600> + $<$:-Wstack-usage=2900> ) if(NOT SANITIZE MATCHES undefined) # RTTI can be disabled except for UBSan which checks vptr integrity. diff --git a/lib/evmone/advanced_analysis.cpp b/lib/evmone/advanced_analysis.cpp index c7b4f6d7..7caeb034 100644 --- a/lib/evmone/advanced_analysis.cpp +++ b/lib/evmone/advanced_analysis.cpp @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "advanced_analysis.hpp" -#include "opcodes_helpers.h" #include namespace evmone::advanced @@ -122,7 +121,14 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept block = BlockAnalysis{analysis.instrs.size() - 1}; break; - case ANY_SMALL_PUSH: + case OP_PUSH1: + case OP_PUSH2: + case OP_PUSH3: + case OP_PUSH4: + case OP_PUSH5: + case OP_PUSH6: + case OP_PUSH7: + case OP_PUSH8: { const auto push_size = static_cast(opcode - OP_PUSH1) + 1; const auto push_end = std::min(code_pos + push_size, code_end); @@ -138,7 +144,30 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept break; } - case ANY_LARGE_PUSH: + case OP_PUSH9: + case OP_PUSH10: + case OP_PUSH11: + case OP_PUSH12: + case OP_PUSH13: + case OP_PUSH14: + case OP_PUSH15: + case OP_PUSH16: + case OP_PUSH17: + case OP_PUSH18: + case OP_PUSH19: + case OP_PUSH20: + case OP_PUSH21: + case OP_PUSH22: + case OP_PUSH23: + case OP_PUSH24: + case OP_PUSH25: + case OP_PUSH26: + case OP_PUSH27: + case OP_PUSH28: + case OP_PUSH29: + case OP_PUSH30: + case OP_PUSH31: + case OP_PUSH32: { const auto push_size = static_cast(opcode - OP_PUSH1) + 1; const auto push_end = code_pos + push_size; @@ -147,7 +176,7 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept const auto push_value_bytes = intx::as_bytes(push_value); auto insert_pos = &push_value_bytes[push_size - 1]; - // Copy bytes to the deticated storage in the order to match native endianness. + // Copy bytes to the dedicated storage in the order to match native endianness. // The condition `code_pos < code_end` is to handle the edge case of PUSH being at // the end of the code with incomplete value bytes. // This condition can be replaced with single `push_end <= code_end` done once before diff --git a/lib/evmone/advanced_analysis.hpp b/lib/evmone/advanced_analysis.hpp index fba3af24..8fcc96b8 100644 --- a/lib/evmone/advanced_analysis.hpp +++ b/lib/evmone/advanced_analysis.hpp @@ -4,6 +4,7 @@ #pragma once #include "execution_state.hpp" +#include "instructions.hpp" #include "instructions_opcodes.hpp" #include #include @@ -31,74 +32,37 @@ struct BlockInfo }; static_assert(sizeof(BlockInfo) == 8); -/// The view/controller for EVM stack. -class Stack -{ -public: - /// The pointer to the top item. - /// This is never null. - uint256* top_item = nullptr; - -private: - /// The pointer to the stack space "bottom". - uint256* m_bottom = nullptr; - -public: - /// Init with the provided stack space. - explicit Stack(uint256* stack_space_bottom) noexcept { reset(stack_space_bottom); } - - /// The current number of items on the stack. - [[nodiscard]] int size() const noexcept { return static_cast(top_item - m_bottom); } - - /// Returns the reference to the top item. - // NOLINTNEXTLINE(readability-make-member-function-const) - [[nodiscard]] uint256& top() noexcept { return *top_item; } - - /// Returns the reference to the stack item on given position from the stack top. - // NOLINTNEXTLINE(readability-make-member-function-const) - [[nodiscard]] uint256& operator[](int index) noexcept { return *(top_item - index); } - - /// Returns the const reference to the stack item on given position from the stack top. - [[nodiscard]] const uint256& operator[](int index) const noexcept - { - return *(top_item - index); - } - - /// Pushes an item on the stack. The stack limit is not checked. - void push(const uint256& item) noexcept { *++top_item = item; } - - /// Returns an item popped from the top of the stack. - uint256 pop() noexcept { return *top_item--; } - - /// Empties the stack by resetting the top item pointer to the new provided stack space. - void reset(uint256* stack_space_bottom) noexcept - { - m_bottom = stack_space_bottom; - top_item = m_bottom; - } -}; /// The execution state specialized for the Advanced interpreter. struct AdvancedExecutionState : ExecutionState { int64_t gas_left = 0; - Stack stack; + + /// Pointer to the stack top. + StackTop stack = stack_space.bottom(); /// The gas cost of the current block. /// /// This is only needed to correctly calculate the "current gas left" value. uint32_t current_block_cost = 0; - AdvancedExecutionState() noexcept : stack{stack_space.bottom()} {} + AdvancedExecutionState() noexcept = default; AdvancedExecutionState(const evmc_message& message, evmc_revision revision, const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code) noexcept - : ExecutionState{message, revision, host_interface, host_ctx, _code}, - gas_left{message.gas}, - stack{stack_space.bottom()} + : ExecutionState{message, revision, host_interface, host_ctx, _code}, gas_left{message.gas} {} + /// Computes the current EVM stack height. + [[nodiscard]] int stack_size() noexcept + { + return static_cast((&stack.top() - stack_space.bottom())); + } + + /// Adjust the EVM stack height by given change. + void adjust_stack_size(int change) noexcept { stack = &stack.top() + change; } + /// Terminates the execution with the given status code. const Instruction* exit(evmc_status_code status_code) noexcept { @@ -113,7 +77,7 @@ struct AdvancedExecutionState : ExecutionState { ExecutionState::reset(message, revision, host_interface, host_ctx, _code, gasparams, _eos_evm_version); gas_left = message.gas; - stack.reset(stack_space.bottom()); + stack = stack_space.bottom(); analysis.advanced = nullptr; // For consistency with previous behavior. current_block_cost = 0; } @@ -136,7 +100,7 @@ using instruction_exec_fn = const Instruction* (*)(const Instruction*, AdvancedE /// /// These intrinsic instructions may be injected to the code in the analysis phase. /// They contain additional and required logic to be executed by the interpreter. -enum intrinsic_opcodes +enum intrinsic_opcodes : uint8_t { /// The BEGINBLOCK instruction. /// @@ -150,7 +114,7 @@ struct OpTableEntry { instruction_exec_fn fn; int16_t gas_cost; - int8_t stack_req; + uint8_t stack_req; int8_t stack_change; }; diff --git a/lib/evmone/advanced_execution.cpp b/lib/evmone/advanced_execution.cpp index 60fa0d13..a1bf93d2 100644 --- a/lib/evmone/advanced_execution.cpp +++ b/lib/evmone/advanced_execution.cpp @@ -46,7 +46,7 @@ evmc_result execute(evmc_vm* /*unused*/, const evmc_host_interface* host, evmc_h const bytes_view container = {code, code_size}; if (is_eof_container(container)) { - if (rev >= EVMC_CANCUN) + if (rev >= EVMC_PRAGUE) { const auto eof1_header = read_valid_eof1_header(container); analysis = analyze(rev, eof1_header.get_code(container, 0)); diff --git a/lib/evmone/advanced_instructions.cpp b/lib/evmone/advanced_instructions.cpp index 282df045..562663cf 100644 --- a/lib/evmone/advanced_instructions.cpp +++ b/lib/evmone/advanced_instructions.cpp @@ -17,22 +17,22 @@ using namespace evmone::instr; template > inline void impl(AdvancedExecutionState& state) noexcept { - CoreFn(state.stack.top_item); - state.stack.top_item += instr::traits[Op].stack_height_change; + CoreFn(state.stack); + state.adjust_stack_size(instr::traits[Op].stack_height_change); } template > inline void impl(AdvancedExecutionState& state) noexcept { - CoreFn(state.stack.top_item, state); - state.stack.top_item += instr::traits[Op].stack_height_change; + CoreFn(state.stack, state); + state.adjust_stack_size(instr::traits[Op].stack_height_change); } template > inline evmc_status_code impl(AdvancedExecutionState& state) noexcept { - const auto status = CoreFn(state.stack.top_item, state); - state.stack.top_item += instr::traits[Op].stack_height_change; + const auto status = CoreFn(state.stack, state); + state.adjust_stack_size(instr::traits[Op].stack_height_change); return status; } @@ -40,17 +40,17 @@ template > inline evmc_status_code impl(AdvancedExecutionState& state) noexcept { - const auto status = CoreFn(state.stack.top_item, state.gas_left, state); - state.stack.top_item += instr::traits[Op].stack_height_change; + const auto status = CoreFn(state.stack, state.gas_left, state); + state.adjust_stack_size(instr::traits[Op].stack_height_change); return status; } template > inline evmc_status_code impl(AdvancedExecutionState& state) noexcept { - const auto status = CoreFn(state.stack.top_item, state.gas_left, state); + const auto status = CoreFn(state.stack, state.gas_left, state); state.gas_left = status.gas_left; - state.stack.top_item += instr::traits[Op].stack_height_change; + state.adjust_stack_size(instr::traits[Op].stack_height_change); return status.status; } @@ -59,15 +59,39 @@ template > +inline Result impl(AdvancedExecutionState& state, code_iterator pos) noexcept +{ + // Stack height adjustment may be omitted. + return CoreFn(state.stack, state.gas_left, state, pos); +} + +template > +inline TermResult impl(AdvancedExecutionState& state, code_iterator pos) noexcept +{ + // Stack height adjustment may be omitted. + return CoreFn(state.stack, state.gas_left, state, pos); } template > inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept { - const auto new_pos = CoreFn(state.stack.top_item, state, pos); - state.stack.top_item += instr::traits[Op].stack_height_change; + const auto new_pos = CoreFn(state.stack, state, pos); + state.adjust_stack_size(instr::traits[Op].stack_height_change); + return new_pos; +} + +template > +inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept +{ + const auto new_pos = CoreFn(state.stack, pos); + state.adjust_stack_size(instr::traits[Op].stack_height_change); return new_pos; } /// @} @@ -79,6 +103,14 @@ inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noex template const Instruction* op(const Instruction* /*instr*/, AdvancedExecutionState& state) noexcept; +/// Wraps the generic instruction implementation to advanced instruction function signature. +template +const Instruction* op(const Instruction* instr, AdvancedExecutionState& state) noexcept; + +/// Wraps the generic instruction implementation to advanced instruction function signature. +template +const Instruction* op(const Instruction* instr, AdvancedExecutionState& state) noexcept; + namespace { using advanced::op; @@ -131,10 +163,9 @@ const Instruction* opx_beginblock(const Instruction* instr, AdvancedExecutionSta if ((state.gas_left -= block.gas_cost) < 0) return state.exit(EVMC_OUT_OF_GAS); - if (static_cast(state.stack.size()) < block.stack_req) + if (const auto stack_size = state.stack_size(); stack_size < block.stack_req) return state.exit(EVMC_STACK_UNDERFLOW); - - if (static_cast(state.stack.size()) + block.stack_max_growth > StackSpace::limit) + else if (stack_size + block.stack_max_growth > StackSpace::limit) return state.exit(EVMC_STACK_OVERFLOW); state.current_block_cost = block.gas_cost; @@ -157,12 +188,12 @@ const Instruction* op_jumpi(const Instruction* instr, AdvancedExecutionState& st if (state.stack[1] != 0) { instr = op_jump(instr, state); // target - state.stack.pop(); // condition + (void)state.stack.pop(); // condition } else { - state.stack.pop(); // target - state.stack.pop(); // condition + (void)state.stack.pop(); // target + (void)state.stack.pop(); // condition instr = opx_beginblock(instr, state); // follow-by block } return instr; @@ -265,9 +296,21 @@ constexpr std::array instruction_implementations = []( table[OP_RJUMPV] = op_undefined; table[OP_CALLF] = op_undefined; table[OP_RETF] = op_undefined; + table[OP_DATALOAD] = op_undefined; + table[OP_DATALOADN] = op_undefined; + table[OP_DATASIZE] = op_undefined; + table[OP_DATACOPY] = op_undefined; + table[OP_RETURNDATALOAD] = op_undefined; + table[OP_EXTCALL] = op_undefined; + table[OP_EXTSTATICCALL] = op_undefined; + table[OP_EXTDELEGATECALL] = op_undefined; + table[OP_JUMPF] = op_undefined; table[OP_DUPN] = op_undefined; table[OP_SWAPN] = op_undefined; + table[OP_EXCHANGE] = op_undefined; + table[OP_EOFCREATE] = op_undefined; + table[OP_RETURNCONTRACT] = op_undefined; return table; }(); diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index 7456f53d..217dff89 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -7,13 +7,11 @@ #include #include #include -#include #include namespace evmone { -using bytes_view = std::basic_string_view; - +using evmc::bytes_view; class ExecutionState; class VM; @@ -24,41 +22,66 @@ class CodeAnalysis public: using JumpdestMap = std::vector; - bytes_view executable_code; ///< Executable code section. - JumpdestMap jumpdest_map; ///< Map of valid jump destinations. - EOF1Header eof_header; ///< The EOF header. - private: + bytes_view m_raw_code; ///< Unmodified full code. + bytes_view m_executable_code; ///< Executable code section. + JumpdestMap m_jumpdest_map; ///< Map of valid jump destinations. + EOF1Header m_eof_header; ///< The EOF header. + /// Padded code for faster legacy code execution. /// If not nullptr the executable_code must point to it. std::unique_ptr m_padded_code; public: + /// Constructor for legacy code. CodeAnalysis(std::unique_ptr padded_code, size_t code_size, JumpdestMap map) - : executable_code{padded_code.get(), code_size}, - jumpdest_map{std::move(map)}, + : m_raw_code{padded_code.get(), code_size}, + m_executable_code{padded_code.get(), code_size}, + m_jumpdest_map{std::move(map)}, m_padded_code{std::move(padded_code)} {} - CodeAnalysis(bytes_view code, EOF1Header header) - : executable_code{code}, eof_header{std::move(header)} + /// Constructor for EOF. + CodeAnalysis(bytes_view container, bytes_view executable_code, EOF1Header header) + : m_raw_code{container}, m_executable_code(executable_code), m_eof_header{std::move(header)} {} + + /// The raw code as stored in accounts or passes as initcode. For EOF this is full container. + [[nodiscard]] bytes_view raw_code() const noexcept { return m_raw_code; } + + /// The pre-processed executable code. This is where interpreter should start execution. + [[nodiscard]] bytes_view executable_code() const noexcept { return m_executable_code; } + + /// Reference to the EOF header. + [[nodiscard]] const EOF1Header& eof_header() const noexcept { return m_eof_header; } + + /// Reference to the EOF data section. May be empty. + [[nodiscard]] bytes_view eof_data() const noexcept { return m_eof_header.get_data(m_raw_code); } + + /// Check if given position is valid jump destination. Use only for legacy code. + [[nodiscard]] bool check_jumpdest(uint64_t position) const noexcept + { + if (position >= m_jumpdest_map.size()) + return false; + return m_jumpdest_map[static_cast(position)]; + } }; -static_assert(std::is_move_constructible_v); -static_assert(std::is_move_assignable_v); -static_assert(!std::is_copy_constructible_v); -static_assert(!std::is_copy_assignable_v); -/// Analyze the code to build the bitmap of valid JUMPDEST locations. -EVMC_EXPORT CodeAnalysis analyze(evmc_revision rev, bytes_view code); +/// Analyze the EVM code in preparation for execution. +/// +/// For legacy code this builds the map of valid JUMPDESTs. +/// If EOF is enabled, it recognizes the EOF code by the code prefix. +/// +/// @param code The reference to the EVM code to be analyzed. +/// @param eof_enabled Should the EOF code prefix be recognized as EOF code? +EVMC_EXPORT CodeAnalysis analyze(bytes_view code, bool eof_enabled); /// Executes in Baseline interpreter using EVMC-compatible parameters. evmc_result execute(evmc_vm* vm, const evmc_host_interface* host, evmc_host_context* ctx, evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) noexcept; -/// Executes in Baseline interpreter on the given external and initialized state. -EVMC_EXPORT evmc_result execute( - const VM&, int64_t gas_limit, ExecutionState& state, const CodeAnalysis& analysis) noexcept; +/// Executes in Baseline interpreter with the pre-processed code. +EVMC_EXPORT evmc_result execute(VM&, const evmc_message& msg, const CodeAnalysis& analysis, ExecutionState& state) noexcept; } // namespace baseline } // namespace evmone diff --git a/lib/evmone/baseline_analysis.cpp b/lib/evmone/baseline_analysis.cpp new file mode 100644 index 00000000..22be340b --- /dev/null +++ b/lib/evmone/baseline_analysis.cpp @@ -0,0 +1,81 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2020 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "baseline.hpp" +#include "eof.hpp" +#include "instructions.hpp" +#include + +namespace evmone::baseline +{ +static_assert(std::is_move_constructible_v); +static_assert(std::is_move_assignable_v); +static_assert(!std::is_copy_constructible_v); +static_assert(!std::is_copy_assignable_v); + +namespace +{ +CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code) +{ + // To find if op is any PUSH opcode (OP_PUSH1 <= op <= OP_PUSH32) + // it can be noticed that OP_PUSH32 is INT8_MAX (0x7f) therefore + // static_cast(op) <= OP_PUSH32 is always true and can be skipped. + static_assert(OP_PUSH32 == std::numeric_limits::max()); + + CodeAnalysis::JumpdestMap map(code.size()); // Allocate and init bitmap with zeros. + for (size_t i = 0; i < code.size(); ++i) + { + const auto op = code[i]; + if (static_cast(op) >= OP_PUSH1) // If any PUSH opcode (see explanation above). + i += op - size_t{OP_PUSH1 - 1}; // Skip PUSH data. + else if (INTX_UNLIKELY(op == OP_JUMPDEST)) + map[i] = true; + } + + return map; +} + +std::unique_ptr pad_code(bytes_view code) +{ + // We need at most 33 bytes of code padding: 32 for possible missing all data bytes of PUSH32 + // at the very end of the code; and one more byte for STOP to guarantee there is a terminating + // instruction at the code end. + constexpr auto padding = 32 + 1; + + auto padded_code = std::make_unique_for_overwrite(code.size() + padding); + std::copy(std::begin(code), std::end(code), padded_code.get()); + std::fill_n(&padded_code[code.size()], padding, uint8_t{OP_STOP}); + return padded_code; +} + + +CodeAnalysis analyze_legacy(bytes_view code) +{ + // TODO: The padded code buffer and jumpdest bitmap can be created with single allocation. + return {pad_code(code), code.size(), analyze_jumpdests(code)}; +} + +CodeAnalysis analyze_eof1(bytes_view container) +{ + auto header = read_valid_eof1_header(container); + + // Extract all code sections as single buffer reference. + // TODO: It would be much easier if header had code_sections_offset and data_section_offset + // with code_offsets[] being relative to code_sections_offset. + const auto code_sections_offset = header.code_offsets[0]; + const auto code_sections_end = size_t{header.code_offsets.back()} + header.code_sizes.back(); + const auto executable_code = + container.substr(code_sections_offset, code_sections_end - code_sections_offset); + + return CodeAnalysis{container, executable_code, std::move(header)}; +} +} // namespace + +CodeAnalysis analyze(bytes_view code, bool eof_enabled) +{ + if (eof_enabled && is_eof_container(code)) + return analyze_eof1(code); + return analyze_legacy(code); +} +} // namespace evmone::baseline diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline_execution.cpp similarity index 77% rename from lib/evmone/baseline.cpp rename to lib/evmone/baseline_execution.cpp index d6cd6194..1bd71a4f 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline_execution.cpp @@ -24,72 +24,6 @@ namespace evmone::baseline { -namespace -{ -CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code) -{ - // To find if op is any PUSH opcode (OP_PUSH1 <= op <= OP_PUSH32) - // it can be noticed that OP_PUSH32 is INT8_MAX (0x7f) therefore - // static_cast(op) <= OP_PUSH32 is always true and can be skipped. - static_assert(OP_PUSH32 == std::numeric_limits::max()); - - CodeAnalysis::JumpdestMap map(code.size()); // Allocate and init bitmap with zeros. - for (size_t i = 0; i < code.size(); ++i) - { - const auto op = code[i]; - if (static_cast(op) >= OP_PUSH1) // If any PUSH opcode (see explanation above). - i += op - size_t{OP_PUSH1 - 1}; // Skip PUSH data. - else if (INTX_UNLIKELY(op == OP_JUMPDEST)) - map[i] = true; - } - - return map; -} - -std::unique_ptr pad_code(bytes_view code) -{ - // We need at most 33 bytes of code padding: 32 for possible missing all data bytes of PUSH32 - // at the very end of the code; and one more byte for STOP to guarantee there is a terminating - // instruction at the code end. - constexpr auto padding = 32 + 1; - - // Using "raw" new operator instead of std::make_unique() to get uninitialized array. - std::unique_ptr padded_code{new uint8_t[code.size() + padding]}; - std::copy(std::begin(code), std::end(code), padded_code.get()); - std::fill_n(&padded_code[code.size()], padding, uint8_t{OP_STOP}); - return padded_code; -} - - -CodeAnalysis analyze_legacy(bytes_view code) -{ - // TODO: The padded code buffer and jumpdest bitmap can be created with single allocation. - return {pad_code(code), code.size(), analyze_jumpdests(code)}; -} - -CodeAnalysis analyze_eof1(bytes_view container) -{ - auto header = read_valid_eof1_header(container); - - // Extract all code sections as single buffer reference. - // TODO: It would be much easier if header had code_sections_offset and data_section_offset - // with code_offsets[] being relative to code_sections_offset. - const auto code_sections_offset = header.code_offsets[0]; - const auto code_sections_end = size_t{header.code_offsets.back()} + header.code_sizes.back(); - const auto executable_code = - container.substr(code_sections_offset, code_sections_end - code_sections_offset); - - return CodeAnalysis{executable_code, std::move(header)}; -} -} // namespace - -CodeAnalysis analyze(evmc_revision rev, bytes_view code) -{ - if (rev < EVMC_CANCUN || !is_eof_container(code)) - return analyze_legacy(code); - return analyze_eof1(code); -} - namespace { /// Checks instruction requirements before execution. @@ -115,7 +49,7 @@ inline evmc_status_code check_requirements(const CostTable& cost_table, int64_t& !instr::has_const_gas_cost(Op) || instr::gas_costs[EVMC_FRONTIER][Op] != instr::undefined, "undefined instructions must not be handled by check_requirements()"); - int64_t gas_cost = instr::gas_costs[EVMC_FRONTIER][Op]; // Init assuming const cost. + auto gas_cost = instr::gas_costs[EVMC_FRONTIER][Op]; // Init assuming const cost. if constexpr (!instr::has_const_gas_cost(Op)) { gas_cost = cost_table[Op]; // If not, load the cost from the table. @@ -194,6 +128,13 @@ struct Position return instr_fn(pos.stack_top, state, pos.code_it); } +[[release_inline]] inline code_iterator invoke( + code_iterator (*instr_fn)(StackTop, code_iterator) noexcept, Position pos, int64_t& /*gas*/, + ExecutionState& /*state*/) noexcept +{ + return instr_fn(pos.stack_top, pos.code_it); +} + [[release_inline]] inline code_iterator invoke( TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, ExecutionState& state) noexcept @@ -203,7 +144,30 @@ struct Position state.status = result.status; return nullptr; } -/// @} + +[[release_inline]] inline code_iterator invoke( + Result (*instr_fn)(StackTop, int64_t, ExecutionState&, code_iterator&) noexcept, Position pos, + int64_t& gas, ExecutionState& state) noexcept +{ + const auto result = instr_fn(pos.stack_top, gas, state, pos.code_it); + gas = result.gas_left; + if (result.status != EVMC_SUCCESS) + { + state.status = result.status; + return nullptr; + } + return pos.code_it; +} + +[[release_inline]] inline code_iterator invoke( + TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&, code_iterator) noexcept, + Position pos, int64_t& gas, ExecutionState& state) noexcept +{ + const auto result = instr_fn(pos.stack_top, gas, state, pos.code_it); + gas = result.gas_left; + state.status = result.status; + return nullptr; +} /// A helper to invoke the instruction implementation of the given opcode Op. template @@ -323,19 +287,19 @@ int64_t dispatch_cgoto( #endif } // namespace -evmc_result execute( - const VM& vm, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept +evmc_result execute(VM& vm, const evmc_message& msg, const CodeAnalysis& analysis, ExecutionState& state) noexcept { - state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. + const auto code = analysis.executable_code(); + auto gas = msg.gas; - const auto code = analysis.executable_code; + state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. - const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version); + const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header().version); auto* tracer = vm.get_tracer(); if (INTX_UNLIKELY(tracer != nullptr)) { - tracer->notify_execution_start(state.rev, *state.msg, analysis.executable_code); + tracer->notify_execution_start(state.rev, *state.msg, code); gas = dispatch(cost_table, state, gas, code.data(), tracer); } else @@ -350,7 +314,6 @@ evmc_result execute( auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_state.cpu_gas_refund() : 0; - int64_t storage_gas_consumed = 0; int64_t storage_gas_refund = 0; int64_t speculative_cpu_gas_consumed = 0; @@ -366,8 +329,13 @@ evmc_result execute( } assert(state.output_size != 0 || state.output_offset == 0); - const auto result = evmc::make_result(state.status, gas_left, gas_refund, storage_gas_consumed, storage_gas_refund, speculative_cpu_gas_consumed, - state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size); + const auto result = + (state.deploy_container.has_value() ? + evmc::make_result(state.status, gas_left, gas_refund, storage_gas_consumed, storage_gas_refund, speculative_cpu_gas_consumed, + state.deploy_container->data(), state.deploy_container->size()) : + evmc::make_result(state.status, gas_left, gas_refund, storage_gas_consumed, storage_gas_refund, speculative_cpu_gas_consumed, + state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, + state.output_size)); if (INTX_UNLIKELY(tracer != nullptr)) tracer->notify_execution_end(result); @@ -379,9 +347,22 @@ evmc_result execute(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_co evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) noexcept { auto vm = static_cast(c_vm); - const auto jumpdest_map = analyze(rev, {code, code_size}); - auto state = - std::make_unique(*msg, rev, *host, ctx, bytes_view{code, code_size}); - return execute(*vm, msg->gas, *state, jumpdest_map); + const bytes_view container{code, code_size}; + const auto eof_enabled = rev >= instr::REV_EOF1; + + // Since EOF validation recurses into subcontainers, it only makes sense to do for top level + // message calls. The condition for `msg->kind` inside differentiates between creation tx code + // (initcode) and already deployed code (runtime). + if (vm->validate_eof && eof_enabled && is_eof_container(container) && msg->depth == 0) + { + const auto container_kind = + (msg->kind == EVMC_EOFCREATE ? ContainerKind::initcode : ContainerKind::runtime); + if (validate_eof(rev, container_kind, container) != EOFValidationError::success) + return evmc_make_result(EVMC_CONTRACT_VALIDATION_FAILURE, 0, 0, 0, 0, 0, nullptr, 0); + } + + const auto code_analysis = analyze(container, eof_enabled); + auto state = std::make_unique(*msg, rev, *host, ctx, container); + return execute(*vm, *msg, code_analysis, *state); } } // namespace evmone::baseline diff --git a/lib/evmone/baseline_instruction_table.cpp b/lib/evmone/baseline_instruction_table.cpp index 2fd6b4b6..5ce155a0 100644 --- a/lib/evmone/baseline_instruction_table.cpp +++ b/lib/evmone/baseline_instruction_table.cpp @@ -9,44 +9,29 @@ namespace evmone::baseline { namespace { -constexpr auto common_cost_tables = []() noexcept { +consteval auto build_cost_tables(bool eof) noexcept +{ std::array tables{}; for (size_t r = EVMC_FRONTIER; r <= EVMC_MAX_REVISION; ++r) { auto& table = tables[r]; - for (size_t i = 0; i < table.size(); ++i) + for (size_t op = 0; op < table.size(); ++op) { - table[i] = instr::gas_costs[r][i]; // Include instr::undefined in the table. + const auto& tr = instr::traits[op]; + const auto since = eof ? tr.eof_since : tr.since; + table[op] = (since && r >= *since) ? instr::gas_costs[r][op] : instr::undefined; } } return tables; -}(); - -constexpr auto legacy_cost_tables = []() noexcept { - auto tables = common_cost_tables; - tables[EVMC_CANCUN][OP_RJUMP] = instr::undefined; - tables[EVMC_CANCUN][OP_RJUMPI] = instr::undefined; - tables[EVMC_CANCUN][OP_RJUMPV] = instr::undefined; - tables[EVMC_CANCUN][OP_CALLF] = instr::undefined; - tables[EVMC_CANCUN][OP_RETF] = instr::undefined; - return tables; -}(); - -constexpr auto eof_cost_tables = []() noexcept { - auto tables = common_cost_tables; - tables[EVMC_CANCUN][OP_JUMP] = instr::undefined; - tables[EVMC_CANCUN][OP_JUMPI] = instr::undefined; - tables[EVMC_CANCUN][OP_PC] = instr::undefined; - tables[EVMC_CANCUN][OP_CALLCODE] = instr::undefined; - tables[EVMC_CANCUN][OP_SELFDESTRUCT] = instr::undefined; - return tables; -}(); +} +constexpr auto LEGACY_COST_TABLES = build_cost_tables(false); +constexpr auto EOF_COST_TABLES = build_cost_tables(true); } // namespace const CostTable& get_baseline_cost_table(evmc_revision rev, uint8_t eof_version) noexcept { - const auto& tables = (eof_version == 0) ? legacy_cost_tables : eof_cost_tables; + const auto& tables = (eof_version == 0) ? LEGACY_COST_TABLES : EOF_COST_TABLES; return tables[rev]; } } // namespace evmone::baseline diff --git a/lib/evmone/constants.hpp b/lib/evmone/constants.hpp new file mode 100644 index 00000000..f3200c0c --- /dev/null +++ b/lib/evmone/constants.hpp @@ -0,0 +1,15 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2021 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +namespace evmone +{ +/// The limit of the size of created contract +/// defined by [EIP-170](https://eips.ethereum.org/EIPS/eip-170) +constexpr auto MAX_CODE_SIZE = 0x6000; + +/// The limit of the size of init codes for contract creation +/// defined by [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) +constexpr auto MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE; +} // namespace evmone diff --git a/lib/evmone/cpu_check.cpp b/lib/evmone/cpu_check.cpp index 8e60630f..5662f2f3 100644 --- a/lib/evmone/cpu_check.cpp +++ b/lib/evmone/cpu_check.cpp @@ -5,10 +5,12 @@ #if defined(__GNUC__) && __GNUC__ >= 12 #define CPU_FEATURE "x86-64-v" STRINGIFY(EVMONE_X86_64_ARCH_LEVEL) #else -// Clang 16 and GCC 11 does not support architecture levels in __builtin_cpu_supports(). -// Use approximations. +// TODO(clang-18): x86 architecture levels are supported in __builtin_cpu_supports() +// since GCC 11 and Clang 18. Use approximations as fallback. #if EVMONE_X86_64_ARCH_LEVEL == 2 #define CPU_FEATURE "sse4.2" +#elif EVMONE_X86_64_ARCH_LEVEL == 3 +#define CPU_FEATURE "avx2" #endif #endif diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 95e6e686..c2a3ed90 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -4,32 +4,40 @@ #include "eof.hpp" #include "baseline_instruction_table.hpp" +#include "constants.hpp" +#include "execution_state.hpp" #include "instructions_traits.hpp" +#include #include #include #include #include #include -#include -#include -#include +#include +#include +#include #include namespace evmone { namespace { -constexpr uint8_t MAGIC[] = {0xef, 0x00}; +constexpr uint8_t MAGIC_BYTES[] = {0xef, 0x00}; +constexpr bytes_view MAGIC{MAGIC_BYTES, std::size(MAGIC_BYTES)}; constexpr uint8_t TERMINATOR = 0x00; constexpr uint8_t TYPE_SECTION = 0x01; constexpr uint8_t CODE_SECTION = 0x02; -constexpr uint8_t DATA_SECTION = 0x03; +constexpr uint8_t CONTAINER_SECTION = 0x03; +constexpr uint8_t DATA_SECTION = 0x04; constexpr uint8_t MAX_SECTION = DATA_SECTION; constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024; +constexpr auto CONTAINER_SECTION_NUMBER_LIMIT = 256; constexpr auto MAX_STACK_HEIGHT = 0x03FF; constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F; constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); +constexpr auto STACK_SIZE_LIMIT = 1024; +constexpr uint8_t NON_RETURNING_FUNCTION = 0x80; using EOFSectionHeaders = std::array, MAX_SECTION + 1>; @@ -37,22 +45,41 @@ size_t eof_header_size(const EOFSectionHeaders& headers) noexcept { const auto non_code_section_count = 2; // type section and data section const auto code_section_count = headers[CODE_SECTION].size(); + const auto container_section_count = headers[CONTAINER_SECTION].size(); constexpr auto non_code_section_header_size = 3; // (SECTION_ID + SIZE) per each section - constexpr auto code_section_size_size = 2; + constexpr auto section_size_size = 2; - return sizeof(MAGIC) + 1 + // 1 version byte - non_code_section_count * non_code_section_header_size + sizeof(CODE_SECTION) + 2 + - code_section_count * code_section_size_size + sizeof(TERMINATOR); + auto header_size = std::size(MAGIC) + 1 + // 1 version byte + non_code_section_count * non_code_section_header_size + + sizeof(CODE_SECTION) + 2 + code_section_count * section_size_size + + sizeof(TERMINATOR); + + if (container_section_count != 0) + { + header_size += sizeof(CONTAINER_SECTION) + 2 + container_section_count * section_size_size; + } + return header_size; } EOFValidationError get_section_missing_error(uint8_t section_id) noexcept { - return static_cast( - static_cast(EOFValidationError::header_terminator_missing) + section_id); + switch (section_id) + { + case TERMINATOR: + return EOFValidationError::header_terminator_missing; + case TYPE_SECTION: + return EOFValidationError::type_section_missing; + case CODE_SECTION: + return EOFValidationError::code_section_missing; + case DATA_SECTION: + return EOFValidationError::data_section_missing; + default: + intx::unreachable(); + } } -std::variant validate_eof_headers(bytes_view container) +std::variant validate_section_headers(bytes_view container) { enum class State { @@ -76,6 +103,10 @@ std::variant validate_eof_headers(bytes_v { section_id = *it++; + // Skip optional sections. + if (section_id != expected_section_id && expected_section_id == CONTAINER_SECTION) + expected_section_id = DATA_SECTION; + if (section_id != expected_section_id) return get_section_missing_error(expected_section_id); @@ -98,7 +129,7 @@ std::variant validate_eof_headers(bytes_v return EOFValidationError::zero_section_size; if (section_num > CODE_SECTION_NUMBER_LIMIT) return EOFValidationError::too_many_code_sections; - expected_section_id = DATA_SECTION; + expected_section_id = CONTAINER_SECTION; state = State::section_size; break; } @@ -106,6 +137,20 @@ std::variant validate_eof_headers(bytes_v expected_section_id = TERMINATOR; state = State::section_size; break; + case CONTAINER_SECTION: + { + if (it >= container_end - 1) + return EOFValidationError::incomplete_section_number; + section_num = read_uint16_be(it); + it += 2; + if (section_num == 0) + return EOFValidationError::zero_section_size; + if (section_num > CONTAINER_SECTION_NUMBER_LIMIT) + return EOFValidationError::too_many_container_sections; + expected_section_id = DATA_SECTION; + state = State::section_size; + break; + } default: assert(false); } @@ -113,7 +158,7 @@ std::variant validate_eof_headers(bytes_v } case State::section_size: { - if (section_id == CODE_SECTION) + if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION) { assert(section_num > 0); // Guaranteed by previous validation step. for (size_t i = 0; i < section_num; ++i) @@ -151,12 +196,16 @@ std::variant validate_eof_headers(bytes_v if (state != State::terminated) return EOFValidationError::section_headers_not_terminated; - const auto section_bodies_size = section_headers[TYPE_SECTION].front() + - std::accumulate(section_headers[CODE_SECTION].begin(), - section_headers[CODE_SECTION].end(), 0) + - section_headers[DATA_SECTION].front(); + const auto section_bodies_without_data = + section_headers[TYPE_SECTION].front() + + std::accumulate( + section_headers[CODE_SECTION].begin(), section_headers[CODE_SECTION].end(), 0) + + std::accumulate(section_headers[CONTAINER_SECTION].begin(), + section_headers[CONTAINER_SECTION].end(), 0); const auto remaining_container_size = container_end - it; - if (section_bodies_size != remaining_container_size) + // Only data section may be truncated, so remaining_container size must be at least + // declared_size_without_data + if (remaining_container_size < section_bodies_without_data) return EOFValidationError::invalid_section_bodies_size; if (section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4) @@ -181,13 +230,14 @@ std::variant, EOFValidationError> validate_types( container[offset], container[offset + 1], read_uint16_be(&container[offset + 2])); } - // check 1st section is (0, 0) - if (types[0].inputs != 0 || types[0].outputs != 0) + // check 1st section is (0, 0x80) + if (types[0].inputs != 0 || types[0].outputs != NON_RETURNING_FUNCTION) return EOFValidationError::invalid_first_section_type; for (const auto& t : types) { - if (t.outputs > OUTPUTS_INPUTS_NUMBER_LIMIT || t.inputs > OUTPUTS_INPUTS_NUMBER_LIMIT) + if ((t.outputs > OUTPUTS_INPUTS_NUMBER_LIMIT && t.outputs != NON_RETURNING_FUNCTION) || + t.inputs > OUTPUTS_INPUTS_NUMBER_LIMIT) return EOFValidationError::inputs_outputs_num_above_limit; if (t.max_stack_height > MAX_STACK_HEIGHT) @@ -197,36 +247,109 @@ std::variant, EOFValidationError> validate_types( return types; } -EOFValidationError validate_instructions(evmc_revision rev, bytes_view code) noexcept +/// Result of validating instructions in a code section. +struct InstructionValidationResult +{ + /// Pairs of (container_index, opcode) of all opcodes referencing subcontainers in this section. + std::vector> subcontainer_references; + /// Set of accessed code section indices. + // TODO: Vector can be used here in case unordered_set causes performance issues. + std::unordered_set accessed_code_sections; +}; + +std::variant validate_instructions( + evmc_revision rev, const EOF1Header& header, ContainerKind kind, size_t code_idx, + bytes_view container) noexcept { + const bytes_view code{header.get_code(container, code_idx)}; assert(!code.empty()); // guaranteed by EOF headers validation const auto& cost_table = baseline::get_baseline_cost_table(rev, 1); + bool is_returning = false; + std::unordered_set accessed_code_sections; + std::vector> subcontainer_references; + for (size_t i = 0; i < code.size(); ++i) { const auto op = code[i]; if (cost_table[op] == instr::undefined) return EOFValidationError::undefined_instruction; + if (i + instr::traits[op].immediate_size >= code.size()) + return EOFValidationError::truncated_instruction; + if (op == OP_RJUMPV) { - if (i + 1 >= code.size()) + const auto count = code[i + 1] + 1; + i += static_cast(1 /* max_index */ + count * 2 /* tbl */); + if (i >= code.size()) return EOFValidationError::truncated_instruction; + } + else if (op == OP_CALLF) + { + const auto fid = read_uint16_be(&code[i + 1]); + if (fid >= header.types.size()) + return EOFValidationError::invalid_code_section_index; + if (header.types[fid].outputs == NON_RETURNING_FUNCTION) + return EOFValidationError::callf_to_non_returning_function; + if (code_idx != fid) + accessed_code_sections.insert(fid); + i += 2; + } + else if (op == OP_RETF) + { + is_returning = true; + static_assert(instr::traits[OP_RETF].immediate_size == 0); + } + else if (op == OP_JUMPF) + { + const auto fid = read_uint16_be(&code[i + 1]); + if (fid >= header.types.size()) + return EOFValidationError::invalid_code_section_index; + // JUMPF into returning function means current function is returning. + if (header.types[fid].outputs != NON_RETURNING_FUNCTION) + is_returning = true; + if (code_idx != fid) + accessed_code_sections.insert(fid); + i += 2; + } + else if (op == OP_DATALOADN) + { + const auto index = read_uint16_be(&code[i + 1]); + if (header.data_size < 32 || index > header.data_size - 32) + return EOFValidationError::invalid_dataloadn_index; + i += 2; + } + else if (op == OP_EOFCREATE || op == OP_RETURNCONTRACT) + { + const auto container_idx = code[i + 1]; + if (container_idx >= header.container_sizes.size()) + return EOFValidationError::invalid_container_section_index; + + if (op == OP_RETURNCONTRACT) + { + if (kind == ContainerKind::runtime) + return EOFValidationError::incompatible_container_kind; + } - const auto count = code[i + 1]; - if (count < 1) - return EOFValidationError::invalid_rjumpv_count; - i += static_cast(1 /* count */ + count * 2 /* tbl */); + subcontainer_references.emplace_back(container_idx, Opcode{op}); + ++i; + } + else if (op == OP_RETURN || op == OP_STOP) + { + if (kind == ContainerKind::initcode) + return EOFValidationError::incompatible_container_kind; } else i += instr::traits[op].immediate_size; - - if (i >= code.size()) - return EOFValidationError::truncated_instruction; } - return EOFValidationError::success; + const auto declared_returning = (header.types[code_idx].outputs != NON_RETURNING_FUNCTION); + if (is_returning != declared_returning) + return EOFValidationError::invalid_non_returning_flag; + + return InstructionValidationResult{subcontainer_references, accessed_code_sections}; } /// Validates that that we don't rjump inside an instruction's immediate. @@ -264,8 +387,8 @@ bool validate_rjump_destinations(bytes_view code) noexcept } else if (op == OP_RJUMPV) { - const auto count = code[i + 1]; - imm_size += size_t{1} /* count */ + count * REL_OFFSET_SIZE /* tbl */; + const auto count = size_t{code[i + 1]} + 1; + imm_size += count * REL_OFFSET_SIZE /* tbl */; const size_t post_pos = i + 1 + imm_size; for (size_t k = 0; k < count * REL_OFFSET_SIZE; k += REL_OFFSET_SIZE) @@ -291,70 +414,136 @@ bool validate_rjump_destinations(bytes_view code) noexcept std::variant validate_max_stack_height( bytes_view code, size_t func_index, const std::vector& code_types) { - assert(!code.empty()); - - // Special values used for detecting errors. + // Special value used for detecting errors. static constexpr int32_t LOC_UNVISITED = -1; // Unvisited byte. - static constexpr int32_t LOC_IMMEDIATE = -2; // Immediate byte. // Stack height in the header is limited to uint16_t, // but keeping larger size for ease of calculation. - std::vector stack_heights(code.size(), LOC_UNVISITED); - stack_heights[0] = code_types[func_index].inputs; + struct StackHeightRange + { + int32_t min = LOC_UNVISITED; + int32_t max = LOC_UNVISITED; - std::stack worklist; - worklist.push(0); + [[nodiscard]] bool visited() const noexcept { return min != LOC_UNVISITED; } + }; - while (!worklist.empty()) - { - const auto i = worklist.top(); - worklist.pop(); + assert(!code.empty()); + + std::vector stack_heights(code.size()); + stack_heights[0] = {code_types[func_index].inputs, code_types[func_index].inputs}; + for (size_t i = 0; i < code.size();) + { const auto opcode = static_cast(code[i]); - auto stack_height_required = instr::traits[opcode].stack_height_required; + int stack_height_required = instr::traits[opcode].stack_height_required; auto stack_height_change = instr::traits[opcode].stack_height_change; + const auto stack_height = stack_heights[i]; + if (!stack_height.visited()) + { + // We reached the code that was neither referenced by previous forward jump, + // nor is part of sequential instruction flow. This is not allowed. + return EOFValidationError::unreachable_instructions; + } + if (opcode == OP_CALLF) { const auto fid = read_uint16_be(&code[i + 1]); - if (fid >= code_types.size()) - return EOFValidationError::invalid_code_section_index; + stack_height_required = code_types[fid].inputs; + + if (stack_height.max + code_types[fid].max_stack_height - stack_height_required > + STACK_SIZE_LIMIT) + return EOFValidationError::stack_overflow; - stack_height_required = static_cast(code_types[fid].inputs); + // Instruction validation ensures target function is returning + assert(code_types[fid].outputs != NON_RETURNING_FUNCTION); stack_height_change = static_cast(code_types[fid].outputs - stack_height_required); } + else if (opcode == OP_JUMPF) + { + const auto fid = read_uint16_be(&code[i + 1]); - auto stack_height = stack_heights[i]; - assert(stack_height != LOC_UNVISITED); + if (stack_height.max + code_types[fid].max_stack_height - code_types[fid].inputs > + STACK_SIZE_LIMIT) + return EOFValidationError::stack_overflow; - if (stack_height < stack_height_required) + if (code_types[fid].outputs == NON_RETURNING_FUNCTION) + { + stack_height_required = code_types[fid].inputs; + } + else + { + if (code_types[func_index].outputs < code_types[fid].outputs) + return EOFValidationError::jumpf_destination_incompatible_outputs; + + stack_height_required = code_types[func_index].outputs + code_types[fid].inputs - + code_types[fid].outputs; + + // JUMPF to returning function requires exact number of stack items + // and is allowed only in constant stack segment. + if (stack_height.max > stack_height_required) + return EOFValidationError::stack_higher_than_outputs_required; + } + } + else if (opcode == OP_RETF) + { + stack_height_required = code_types[func_index].outputs; + // RETF allowed only in constant stack segment + if (stack_height.max > stack_height_required) + return EOFValidationError::stack_higher_than_outputs_required; + } + else if (opcode == OP_DUPN) + stack_height_required = code[i + 1] + 1; + else if (opcode == OP_SWAPN) + stack_height_required = code[i + 1] + 2; + else if (opcode == OP_EXCHANGE) + { + const auto n = (code[i + 1] >> 4) + 1; + const auto m = (code[i + 1] & 0x0F) + 1; + stack_height_required = n + m + 1; + } + + if (stack_height.min < stack_height_required) return EOFValidationError::stack_underflow; - stack_height += stack_height_change; + const StackHeightRange next_stack_height{ + stack_height.min + stack_height_change, stack_height.max + stack_height_change}; // Determine size of immediate, including the special case of RJUMPV. const size_t imm_size = (opcode == OP_RJUMPV) ? - (1 + /*count*/ size_t{code[i + 1]} * REL_OFFSET_SIZE) : + (1 + /*count*/ (size_t{code[i + 1]} + 1) * REL_OFFSET_SIZE) : instr::traits[opcode].immediate_size; - // Mark immediate locations. - std::fill_n(&stack_heights[i + 1], imm_size, LOC_IMMEDIATE); - // Validates the successor instruction and updates its stack height. - const auto validate_successor = [&stack_heights, &worklist](size_t successor_offset, - int32_t expected_stack_height) { + const auto visit_successor = [&stack_heights](size_t current_offset, + size_t successor_offset, + StackHeightRange required_stack_height) { auto& successor_stack_height = stack_heights[successor_offset]; - if (successor_stack_height == LOC_UNVISITED) + if (successor_offset <= current_offset) // backwards jump { - successor_stack_height = expected_stack_height; - worklist.push(successor_offset); - return true; + // successor_offset == current_offset case is possible only with jump into the same + // jump instruction, e.g. RJUMP(-3), so it is technically a backwards jump, too. + assert(successor_stack_height.visited()); + // The spec could have been relaxed to + // return successor_stack_height.min >= required_stack_height.min && + // successor_stack_height.max <= required_stack_height.max; + // but it was decided to have strict equality for simplicity. + return successor_stack_height.min == required_stack_height.min && + successor_stack_height.max == required_stack_height.max; } - else - return successor_stack_height == expected_stack_height; + else if (!successor_stack_height.visited()) // forwards jump, new target + successor_stack_height = required_stack_height; + else // forwards jump, target known + { + successor_stack_height.min = + std::min(required_stack_height.min, successor_stack_height.min); + successor_stack_height.max = + std::max(required_stack_height.max, successor_stack_height.max); + } + return true; }; const auto next = i + imm_size + 1; // Offset of the next instruction (may be invalid). @@ -364,7 +553,7 @@ std::variant validate_max_stack_height( { if (next >= code.size()) return EOFValidationError::no_terminating_instruction; - if (!validate_successor(next, stack_height)) + if (!visit_successor(i, next, next_stack_height)) return EOFValidationError::stack_height_mismatch; } @@ -373,38 +562,196 @@ std::variant validate_max_stack_height( { const auto target_rel_offset = read_int16_be(&code[i + 1]); const auto target = static_cast(i) + target_rel_offset + 3; - if (!validate_successor(static_cast(target), stack_height)) + if (!visit_successor(i, static_cast(target), next_stack_height)) return EOFValidationError::stack_height_mismatch; } else if (opcode == OP_RJUMPV) { - const auto count = code[i + 1]; + const auto max_index = code[i + 1]; // Insert all jump targets. - for (size_t k = 0; k < count; ++k) + for (size_t k = 0; k <= max_index; ++k) { const auto target_rel_offset = read_int16_be(&code[i + k * REL_OFFSET_SIZE + 2]); const auto target = static_cast(next) + target_rel_offset; - if (!validate_successor(static_cast(target), stack_height)) + if (!visit_successor(i, static_cast(target), next_stack_height)) return EOFValidationError::stack_height_mismatch; } } - else if (opcode == OP_RETF && stack_height != code_types[func_index].outputs) - return EOFValidationError::non_empty_stack_on_terminating_instruction; + + i = next; } - const auto max_stack_height = *std::max_element(stack_heights.begin(), stack_heights.end()); + const auto max_stack_height_it = std::max_element(stack_heights.begin(), stack_heights.end(), + [](StackHeightRange lhs, StackHeightRange rhs) noexcept { return lhs.max < rhs.max; }); + return max_stack_height_it->max; +} + +EOFValidationError validate_eof1( + evmc_revision rev, ContainerKind main_container_kind, bytes_view main_container) noexcept +{ + struct ContainerValidation + { + bytes_view bytes; + ContainerKind kind; + }; + + if (main_container.size() > MAX_INITCODE_SIZE) + return EOFValidationError::container_size_above_limit; - if (std::find(stack_heights.begin(), stack_heights.end(), LOC_UNVISITED) != stack_heights.end()) - return EOFValidationError::unreachable_instructions; + // Queue of containers left to process + std::queue container_queue; + + container_queue.push({main_container, main_container_kind}); + + while (!container_queue.empty()) + { + const auto& [container, container_kind] = container_queue.front(); + + // Validate header + auto error_or_header = validate_header(rev, container); + if (const auto* error = std::get_if(&error_or_header)) + return *error; - return max_stack_height; + auto& header = std::get(error_or_header); + + if (container.size() > static_cast(header.data_offset) + header.data_size) + return EOFValidationError::invalid_section_bodies_size; + + // Validate code sections + std::vector visited_code_sections(header.code_sizes.size()); + std::queue code_sections_queue({0}); + + const auto subcontainer_count = header.container_sizes.size(); + std::vector subcontainer_referenced_by_eofcreate(subcontainer_count, false); + std::vector subcontainer_referenced_by_returncontract(subcontainer_count, false); + + while (!code_sections_queue.empty()) + { + const auto code_idx = code_sections_queue.front(); + code_sections_queue.pop(); + + if (visited_code_sections[code_idx]) + continue; + + visited_code_sections[code_idx] = true; + + // Validate instructions + const auto instr_validation_result_or_error = + validate_instructions(rev, header, container_kind, code_idx, container); + if (const auto* error = + std::get_if(&instr_validation_result_or_error)) + return *error; + + const auto& [subcontainer_references, accessed_code_sections] = + std::get(instr_validation_result_or_error); + + // Mark what instructions referenced which subcontainers. + for (const auto& [index, opcode] : subcontainer_references) + { + if (opcode == OP_EOFCREATE) + subcontainer_referenced_by_eofcreate[index] = true; + else if (opcode == OP_RETURNCONTRACT) + subcontainer_referenced_by_returncontract[index] = true; + else + intx::unreachable(); + } + + // TODO(C++23): can use push_range() + for (const auto section_id : accessed_code_sections) + code_sections_queue.push(section_id); + + // Validate jump destinations + if (!validate_rjump_destinations(header.get_code(container, code_idx))) + return EOFValidationError::invalid_rjump_destination; + + // Validate stack + auto msh_or_error = validate_max_stack_height( + header.get_code(container, code_idx), code_idx, header.types); + if (const auto* error = std::get_if(&msh_or_error)) + return *error; + if (std::get(msh_or_error) != header.types[code_idx].max_stack_height) + return EOFValidationError::invalid_max_stack_height; + } + + if (std::find(visited_code_sections.begin(), visited_code_sections.end(), false) != + visited_code_sections.end()) + return EOFValidationError::unreachable_code_sections; + + // Check if truncated data section is allowed. + if (!header.has_full_data(container.size())) + { + if (main_container == container) + return EOFValidationError::toplevel_container_truncated; + if (container_kind == ContainerKind::initcode) + return EOFValidationError::eofcreate_with_truncated_container; + } + + // Enqueue subcontainers + for (size_t subcont_idx = 0; subcont_idx < subcontainer_count; ++subcont_idx) + { + const bytes_view subcontainer{header.get_container(container, subcont_idx)}; + + const bool eofcreate = subcontainer_referenced_by_eofcreate[subcont_idx]; + const bool returncontract = subcontainer_referenced_by_returncontract[subcont_idx]; + + if (eofcreate && returncontract) + return EOFValidationError::ambiguous_container_kind; + if (!eofcreate && !returncontract) + return EOFValidationError::unreferenced_subcontainer; + + const auto subcontainer_kind = + (eofcreate ? ContainerKind::initcode : ContainerKind::runtime); + assert(subcontainer_kind == ContainerKind::initcode || returncontract); + + container_queue.push({subcontainer, subcontainer_kind}); + } + + container_queue.pop(); + } + + return EOFValidationError::success; +} +} // namespace + + +size_t EOF1Header::data_size_position() const noexcept +{ + const auto num_code_sections = code_sizes.size(); + const auto num_container_sections = container_sizes.size(); + return std::size(MAGIC) + 1 + // magic + version + 3 + // type section kind + size + 3 + 2 * num_code_sections + // code sections kind + count + sizes + // container sections kind + count + sizes + (num_container_sections != 0 ? 3 + 2 * num_container_sections : 0) + + 1; // data section kind } -std::variant validate_eof1( +bool is_eof_container(bytes_view container) noexcept +{ + return container.starts_with(MAGIC); +} + +std::variant validate_header( evmc_revision rev, bytes_view container) noexcept { - const auto section_headers_or_error = validate_eof_headers(container); + if (!is_eof_container(container)) + return EOFValidationError::invalid_prefix; + + const auto version = get_eof_version(container); + if (version != 1) + return EOFValidationError::eof_version_unknown; + + if (rev < EVMC_PRAGUE) + return EOFValidationError::eof_version_unknown; + + // `offset` variable handled below is known to not be greater than the container size, as + // checked in `validate_section_headers`. Combined with the requirement for the container + // size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called), + // this allows us to cast `offset` to narrower integers. + assert(container.size() <= MAX_INITCODE_SIZE); + + const auto section_headers_or_error = validate_section_headers(container); if (const auto* error = std::get_if(§ion_headers_or_error)) return *error; @@ -423,6 +770,7 @@ std::variant validate_eof1( std::vector code_offsets; const auto type_section_size = section_headers[TYPE_SECTION][0]; auto offset = header_size + type_section_size; + for (const auto code_size : code_sizes) { assert(offset <= std::numeric_limits::max()); @@ -430,33 +778,28 @@ std::variant validate_eof1( offset += code_size; } - EOF1Header header{container[2], code_sizes, code_offsets, data_size, types}; - - for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx) + const auto& container_sizes = section_headers[CONTAINER_SECTION]; + std::vector container_offsets; + for (const auto container_size : container_sizes) { - const auto error_instr = validate_instructions(rev, header.get_code(container, code_idx)); - if (error_instr != EOFValidationError::success) - return error_instr; - - if (!validate_rjump_destinations(header.get_code(container, code_idx))) - return EOFValidationError::invalid_rjump_destination; - - auto msh_or_error = - validate_max_stack_height(header.get_code(container, code_idx), code_idx, header.types); - if (const auto* error = std::get_if(&msh_or_error)) - return *error; - if (std::get(msh_or_error) != header.types[code_idx].max_stack_height) - return EOFValidationError::invalid_max_stack_height; + assert(offset <= std::numeric_limits::max()); + container_offsets.emplace_back(static_cast(offset)); + offset += container_size; } - return header; -} - -} // namespace - -bool is_eof_container(bytes_view container) noexcept -{ - return container.size() > 1 && container[0] == MAGIC[0] && container[1] == MAGIC[1]; + assert(offset <= std::numeric_limits::max()); + const auto data_offset = static_cast(offset); + + return EOF1Header{ + .version = container[2], + .code_sizes = code_sizes, + .code_offsets = code_offsets, + .data_size = data_size, + .data_offset = data_offset, + .container_sizes = container_sizes, + .container_offsets = container_offsets, + .types = types, + }; } /// This function expects the prefix and version to be valid, as it ignores it. @@ -467,7 +810,7 @@ EOF1Header read_valid_eof1_header(bytes_view container) while (*it != TERMINATOR) { const auto section_id = *it++; - if (section_id == CODE_SECTION) + if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION) { const auto code_section_num = read_uint16_be(it); it += 2; @@ -509,36 +852,54 @@ EOF1Header read_valid_eof1_header(bytes_view container) header.data_size = section_headers[DATA_SECTION][0]; + header.container_sizes = section_headers[CONTAINER_SECTION]; + auto container_offset = code_offset; + for (const auto container_size : header.container_sizes) + { + header.container_offsets.emplace_back(static_cast(container_offset)); + container_offset += container_size; + } + + header.data_offset = static_cast(container_offset); + return header; } -uint8_t get_eof_version(bytes_view container) noexcept +bool append_data_section(bytes& container, bytes_view aux_data) { - return (container.size() >= 3 && container[0] == MAGIC[0] && container[1] == MAGIC[1]) ? - container[2] : - 0; -} + const auto header = read_valid_eof1_header(container); -EOFValidationError validate_eof(evmc_revision rev, bytes_view container) noexcept -{ - if (!is_eof_container(container)) - return EOFValidationError::invalid_prefix; + // Assert we don't need to trim off the bytes beyond the declared data section first. + assert(container.size() <= header.data_offset + header.data_size); - const auto version = get_eof_version(container); + const auto new_data_size = container.size() - header.data_offset + aux_data.size(); + if (new_data_size > std::numeric_limits::max()) + return false; - if (version == 1) - { - if (rev < EVMC_CANCUN) - return EOFValidationError::eof_version_unknown; + // Check that appended data size is greater or equal of what header declaration expects. + if (new_data_size < header.data_size) + return false; - const auto header_or_error = validate_eof1(rev, container); - if (const auto* error = std::get_if(&header_or_error)) - return *error; - else - return EOFValidationError::success; - } - else - return EOFValidationError::eof_version_unknown; + // Appending aux_data to the end, assuming data section is always the last one. + container.append(aux_data); + + // Update data size + const auto data_size_pos = header.data_size_position(); + container[data_size_pos] = static_cast(new_data_size >> 8); + container[data_size_pos + 1] = static_cast(new_data_size); + + return true; +} + +uint8_t get_eof_version(bytes_view container) noexcept +{ + return (is_eof_container(container) && container.size() >= 3) ? container[2] : 0; +} + +EOFValidationError validate_eof( + evmc_revision rev, ContainerKind kind, bytes_view container) noexcept +{ + return validate_eof1(rev, kind, container); } std::string_view get_error_message(EOFValidationError err) noexcept @@ -547,12 +908,8 @@ std::string_view get_error_message(EOFValidationError err) noexcept { case EOFValidationError::success: return "success"; - case EOFValidationError::starts_with_format: - return "starts_with_format"; case EOFValidationError::invalid_prefix: return "invalid_prefix"; - case EOFValidationError::eof_version_mismatch: - return "eof_version_mismatch"; case EOFValidationError::eof_version_unknown: return "eof_version_unknown"; case EOFValidationError::incomplete_section_size: @@ -573,12 +930,12 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "section_headers_not_terminated"; case EOFValidationError::invalid_section_bodies_size: return "invalid_section_bodies_size"; + case EOFValidationError::unreachable_code_sections: + return "unreachable_code_sections"; case EOFValidationError::undefined_instruction: return "undefined_instruction"; case EOFValidationError::truncated_instruction: return "truncated_instruction"; - case EOFValidationError::invalid_rjumpv_count: - return "invalid_rjumpv_count"; case EOFValidationError::invalid_rjump_destination: return "invalid_rjump_destination"; case EOFValidationError::too_many_code_sections: @@ -597,17 +954,49 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "no_terminating_instruction"; case EOFValidationError::stack_height_mismatch: return "stack_height_mismatch"; - case EOFValidationError::non_empty_stack_on_terminating_instruction: - return "non_empty_stack_on_terminating_instruction"; + case EOFValidationError::stack_higher_than_outputs_required: + return "stack_higher_than_outputs_required"; case EOFValidationError::unreachable_instructions: return "unreachable_instructions"; case EOFValidationError::stack_underflow: return "stack_underflow"; + case EOFValidationError::stack_overflow: + return "stack_overflow"; case EOFValidationError::invalid_code_section_index: return "invalid_code_section_index"; + case EOFValidationError::invalid_dataloadn_index: + return "invalid_dataloadn_index"; + case EOFValidationError::jumpf_destination_incompatible_outputs: + return "jumpf_destination_incompatible_outputs"; + case EOFValidationError::invalid_non_returning_flag: + return "invalid_non_returning_flag"; + case EOFValidationError::callf_to_non_returning_function: + return "callf_to_non_returning_function"; + case EOFValidationError::too_many_container_sections: + return "too_many_container_sections"; + case EOFValidationError::invalid_container_section_index: + return "invalid_container_section_index"; + case EOFValidationError::eofcreate_with_truncated_container: + return "eofcreate_with_truncated_container"; + case EOFValidationError::toplevel_container_truncated: + return "toplevel_container_truncated"; + case EOFValidationError::ambiguous_container_kind: + return "ambiguous_container_kind"; + case EOFValidationError::incompatible_container_kind: + return "incompatible_container_kind"; + case EOFValidationError::container_size_above_limit: + return "container_size_above_limit"; + case EOFValidationError::unreferenced_subcontainer: + return "unreferenced_subcontainer"; case EOFValidationError::impossible: return "impossible"; } return ""; } + +std::ostream& operator<<(std::ostream& os, EOFValidationError err) noexcept +{ + os << get_error_message(err); + return os; +} } // namespace evmone diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index 1fad7324..36c5b90c 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -3,17 +3,20 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include #include #include #include #include +#include #include namespace evmone { -using bytes_view = std::basic_string_view; +using evmc::bytes; +using evmc::bytes_view; struct EOFCodeType { @@ -37,7 +40,18 @@ struct EOF1Header /// Offset of every code section from the beginning of the EOF container. std::vector code_offsets; + /// Size of the data section. + /// In case of deploy container it is the minimal data size of the container that will be + /// deployed, taking into account part of data appended at deploy-time (static_aux_data). + /// In this case the size of data section present in current container can be less than + /// @data_size. uint16_t data_size = 0; + /// Offset of data container section start. + uint16_t data_offset = 0; + /// Size of every container section. + std::vector container_sizes; + /// Offset of every container section start; + std::vector container_offsets; std::vector types; @@ -47,6 +61,31 @@ struct EOF1Header assert(code_idx < code_offsets.size()); return container.substr(code_offsets[code_idx], code_sizes[code_idx]); } + + /// A helper to extract reference to the data section. + [[nodiscard]] bytes_view get_data(bytes_view container) const noexcept + { + return container.substr(data_offset); + } + + /// A helper to check whether the container has data section body size equal to declare size. + [[nodiscard]] bool has_full_data(size_t container_size) const noexcept + { + // Containers with truncated data section cannot be initcontainers. + const auto truncated_data = static_cast(data_offset + data_size) > container_size; + return !truncated_data; + } + + /// A helper to extract reference to a specific container section. + [[nodiscard]] bytes_view get_container( + bytes_view container, size_t container_idx) const noexcept + { + assert(container_idx < container_offsets.size()); + return container.substr(container_offsets[container_idx], container_sizes[container_idx]); + } + + /// Offset of the data section size value in the header. + [[nodiscard]] size_t data_size_position() const noexcept; }; /// Checks if code starts with EOF FORMAT + MAGIC, doesn't validate the format. @@ -56,12 +95,14 @@ struct EOF1Header /// (must be true for all EOF contracts on-chain) [[nodiscard]] EVMC_EXPORT EOF1Header read_valid_eof1_header(bytes_view container); +/// Modifies container by appending aux_data to data section and updating data section size +/// in the header. +bool append_data_section(bytes& container, bytes_view aux_data); + enum class EOFValidationError { success, - starts_with_format, invalid_prefix, - eof_version_mismatch, eof_version_unknown, incomplete_section_size, @@ -73,9 +114,9 @@ enum class EOFValidationError zero_section_size, section_headers_not_terminated, invalid_section_bodies_size, + unreachable_code_sections, undefined_instruction, truncated_instruction, - invalid_rjumpv_count, invalid_rjump_destination, too_many_code_sections, invalid_type_section_size, @@ -83,27 +124,55 @@ enum class EOFValidationError invalid_max_stack_height, no_terminating_instruction, stack_height_mismatch, - non_empty_stack_on_terminating_instruction, + stack_higher_than_outputs_required, max_stack_height_above_limit, inputs_outputs_num_above_limit, unreachable_instructions, stack_underflow, + stack_overflow, invalid_code_section_index, + invalid_dataloadn_index, + jumpf_destination_incompatible_outputs, + invalid_non_returning_flag, + callf_to_non_returning_function, + too_many_container_sections, + invalid_container_section_index, + eofcreate_with_truncated_container, + toplevel_container_truncated, + ambiguous_container_kind, + incompatible_container_kind, + container_size_above_limit, + unreferenced_subcontainer, impossible, }; +enum class ContainerKind : uint8_t +{ + /// Container that uses RETURNCONTRACT. Can be used by EOFCREATE/Creation transaction. + initcode, + /// Container that uses STOP/RETURN. Can be returned by RETURNCONTRACT. + runtime, +}; + /// Determines the EOF version of the container by inspecting container's EOF prefix. /// If the prefix is missing or invalid, 0 is returned meaning legacy code. [[nodiscard]] uint8_t get_eof_version(bytes_view container) noexcept; +/// Validates the header and returns its representation if successful. +[[nodiscard]] EVMC_EXPORT std::variant validate_header( + evmc_revision rev, bytes_view container) noexcept; + /// Validates whether given container is a valid EOF according to the rules of given revision. [[nodiscard]] EVMC_EXPORT EOFValidationError validate_eof( - evmc_revision rev, bytes_view container) noexcept; + evmc_revision rev, ContainerKind kind, bytes_view container) noexcept; /// Returns the error message corresponding to an error code. [[nodiscard]] EVMC_EXPORT std::string_view get_error_message(EOFValidationError err) noexcept; +/// Output operator for EOFValidationError. +EVMC_EXPORT std::ostream& operator<<(std::ostream& os, EOFValidationError err) noexcept; + /// Loads big endian int16_t from data. Unsafe. /// TODO: Move it to intx inline int16_t read_int16_be(const uint8_t* it) noexcept diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index 96358829..1bd737d3 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -5,10 +5,9 @@ #include #include +#include #include #include -#include - #include "instructions_traits.hpp" namespace evmone @@ -18,7 +17,6 @@ struct StorageStoreCost int64_t gas_cost; int64_t gas_refund; }; - namespace advanced { struct AdvancedCodeAnalysis; @@ -28,42 +26,75 @@ namespace baseline class CodeAnalysis; } -using uint256 = intx::uint256; -using bytes = std::basic_string; -using bytes_view = std::basic_string_view; +using evmc::bytes; +using evmc::bytes_view; +using intx::uint256; /// Provides memory for EVM stack. class StackSpace { + static uint256* allocate() noexcept + { + static constexpr auto alignment = sizeof(uint256); + static constexpr auto size = limit * sizeof(uint256); +#ifdef _MSC_VER + // MSVC doesn't support aligned_alloc() but _aligned_malloc() can be used instead. + const auto p = _aligned_malloc(size, alignment); +#else + const auto p = std::aligned_alloc(alignment, size); +#endif + return static_cast(p); + } + + struct Deleter + { + // TODO(C++23): static + void operator()(void* p) noexcept + { +#ifdef _MSC_VER + // For MSVC the _aligned_malloc() must be paired with _aligned_free(). + _aligned_free(p); +#else + std::free(p); +#endif + } + }; + + /// The storage allocated for maximum possible number of items. + /// Items are aligned to 256 bits for better packing in cache lines. + std::unique_ptr m_stack_space; + public: /// The maximum number of EVM stack items. static constexpr auto limit = 1024; + StackSpace() noexcept : m_stack_space{allocate()} {} + /// Returns the pointer to the "bottom", i.e. below the stack space. [[nodiscard, clang::no_sanitize("bounds")]] uint256* bottom() noexcept { - return m_stack_space - 1; + return m_stack_space.get() - 1; } - -private: - /// The storage allocated for maximum possible number of items. - /// Items are aligned to 256 bits for better packing in cache lines. - alignas(sizeof(uint256)) uint256 m_stack_space[limit]; }; /// The EVM memory. /// /// The implementations uses initial allocation of 4k and then grows capacity with 2x factor. -/// Some benchmarks has been done to confirm 4k is ok-ish value. +/// Some benchmarks have been done to confirm 4k is ok-ish value. class Memory { /// The size of allocation "page". static constexpr size_t page_size = 4 * 1024; - /// Pointer to allocated memory. - uint8_t* m_data = nullptr; + struct FreeDeleter + { + void operator()(uint8_t* p) const noexcept { std::free(p); } + }; + + /// Owned pointer to allocated memory. + std::unique_ptr m_data; /// The "virtual" size of the memory. size_t m_size = 0; @@ -75,8 +106,8 @@ class Memory void allocate_capacity() noexcept { - m_data = static_cast(std::realloc(m_data, m_capacity)); - if (m_data == nullptr) + m_data.reset(static_cast(std::realloc(m_data.release(), m_capacity))); + if (!m_data) [[unlikely]] handle_out_of_memory(); } @@ -84,18 +115,12 @@ class Memory /// Creates Memory object with initial capacity allocation. Memory() noexcept { allocate_capacity(); } - /// Frees all allocated memory. - ~Memory() noexcept { std::free(m_data); } - - Memory(const Memory&) = delete; - Memory& operator=(const Memory&) = delete; - uint8_t& operator[](size_t index) noexcept { return m_data[index]; } - [[nodiscard]] const uint8_t* data() const noexcept { return m_data; } + [[nodiscard]] const uint8_t* data() const noexcept { return m_data.get(); } [[nodiscard]] size_t size() const noexcept { return m_size; } - /// Grows the memory to the given size. The extend is filled with zeros. + /// Grows the memory to the given size. The extent is filled with zeros. /// /// @param new_size New memory size. Must be larger than the current size and multiple of 32. void grow(size_t new_size) noexcept @@ -118,7 +143,7 @@ class Memory allocate_capacity(); } - std::memset(m_data + m_size, 0, new_size - m_size); + std::memset(&m_data[m_size], 0, new_size - m_size); m_size = new_size; } @@ -128,7 +153,7 @@ class Memory struct gas_parameters { - using storage_cost_t = std::array; + using storage_cost_t = std::array; gas_parameters(){} @@ -145,6 +170,19 @@ struct gas_parameters { uint64_t G_codedeposit = 200; uint64_t G_sset = 20000; + static gas_parameters apply_discount_factor(const intx::uint256& inclusion_price, const uint64_t base_fee_per_gas, const uint64_t storage_price, const evmone::gas_parameters& g) { + //storage_gas_discount_factor = (storage_price)/(base_price + inclusion_price) + const intx::uint256 num(storage_price); + const intx::uint256 den(intx::uint256(base_fee_per_gas) + intx::uint256(inclusion_price)); + gas_parameters out; + out.G_txnewaccount = static_cast((num*g.G_txnewaccount)/den); + out.G_newaccount = static_cast((num*g.G_newaccount)/den); + out.G_txcreate = static_cast((num*g.G_txcreate)/den); + out.G_codedeposit = static_cast((num*g.G_codedeposit)/den); + out.G_sset = static_cast((num*g.G_sset)/den); + return out; + } + const storage_cost_t& get_storage_cost(uint64_t version) { if(!storage_cost.has_value()) { storage_cost = generate_storage_cost_table(version); @@ -162,7 +200,7 @@ struct gas_parameters { storage_cost_t st; if( version >= 3) { int64_t cpu_gas_to_change_slot = reset - warm_access; //cpu cost of adding or removing or mutating a slot in the db - int64_t storage_gas_to_add_slot = set - reset; //storage cost of adding a new slot into the db + int64_t storage_gas_to_add_slot = set; //storage cost of adding a new slot into the db st[EVMC_STORAGE_ASSIGNED] = { 0, 0}; st[EVMC_STORAGE_ADDED] = { cpu_gas_to_change_slot, storage_gas_to_add_slot}; @@ -220,11 +258,16 @@ struct gas_state_t { return storage_gas_refund_; } + int64_t calc_apply_storage_gas_delta(int64_t storage_gas_delta) const { + const int64_t d = storage_gas_delta - storage_gas_refund_; + return std::max(d, decltype(d){0}); + } + int64_t apply_storage_gas_delta(int64_t storage_gas_delta){ if (eos_evm_version_ >= 3) { int64_t d = storage_gas_delta - storage_gas_refund_; - storage_gas_refund_ = std::max(-d, 0l); - const auto gas_consumed = std::max(d, 0l); + storage_gas_refund_ = std::max(-d, decltype(d){0}); + const auto gas_consumed = std::max(d, decltype(d){0}); storage_gas_consumed_ += gas_consumed; return gas_consumed; } @@ -234,8 +277,8 @@ struct gas_state_t { int64_t apply_speculative_cpu_gas_delta(int64_t cpu_gas_delta) { if (eos_evm_version_ >= 3) { int64_t d = cpu_gas_delta - cpu_gas_refund_; - cpu_gas_refund_ = std::max(-d, 0l); - const auto gas_consumed = std::max(d, 0l); + cpu_gas_refund_ = std::max(-d, decltype(d){0}); + const auto gas_consumed = std::max(d, decltype(d){0}); speculative_cpu_gas_consumed_ += gas_consumed; return gas_consumed; } @@ -262,13 +305,13 @@ struct gas_state_t { int64_t collapse() { const auto s = std::min(storage_gas_consumed_, storage_gas_refund_); const auto x = storage_gas_consumed_ - storage_gas_refund_; - storage_gas_consumed_ = std::max(x, 0l); - storage_gas_refund_ = std::max(-x, 0l); + storage_gas_consumed_ = std::max(x, decltype(x){0}); + storage_gas_refund_ = std::max(-x, decltype(x){0}); const auto c = std::min(speculative_cpu_gas_consumed_, cpu_gas_refund_); const auto y = speculative_cpu_gas_consumed_ - cpu_gas_refund_; - speculative_cpu_gas_consumed_ = std::max(y, 0l); - cpu_gas_refund_ = std::max(-y, 0l); + speculative_cpu_gas_consumed_ = std::max(y, decltype(y){0}); + cpu_gas_refund_ = std::max(-y, decltype(y){0}); return s + c; } @@ -300,8 +343,7 @@ struct gas_state_t { class ExecutionState { public: - gas_state_t gas_state; - + int64_t gas_refund = 0; Memory memory; const evmc_message* msg = nullptr; evmc::HostContext host; @@ -317,8 +359,12 @@ class ExecutionState size_t output_offset = 0; size_t output_size = 0; + /// Container to be deployed returned from RETURNCONTRACT, used only inside EOFCREATE execution. + std::optional deploy_container; + uint64_t eos_evm_version=0; gas_parameters gas_params; + gas_state_t gas_state; private: evmc_tx_context m_tx = {}; @@ -352,6 +398,7 @@ class ExecutionState const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code, gas_parameters _gas_params, uint64_t _eos_evm_version) noexcept { + gas_refund = 0; memory.clear(); msg = &message; host = {host_interface, host_ctx}; @@ -361,7 +408,9 @@ class ExecutionState status = EVMC_SUCCESS; output_offset = 0; output_size = 0; + deploy_container = {}; m_tx = {}; + call_stack = {}; gas_params = _gas_params; eos_evm_version = _eos_evm_version; gas_state.reset(_eos_evm_version, 0, 0, 0, 0); diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 9d5b5896..dd5b3242 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -63,6 +63,13 @@ inline constexpr int64_t num_words(uint64_t size_in_bytes) noexcept return static_cast((size_in_bytes + (word_size - 1)) / word_size); } +/// Computes gas cost of copying the given amount of bytes to/from EVM memory. +inline constexpr int64_t copy_cost(uint64_t size_in_bytes) noexcept +{ + constexpr auto WordCopyCost = 3; + return num_words(size_in_bytes) * WordCopyCost; +} + /// Grows EVM memory and checks its cost. /// /// This function should not be inlined because this may affect other inlining decisions: @@ -435,8 +442,7 @@ inline Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& sta auto s = static_cast(size); auto copy_size = std::min(s, state.msg->input_size - src); - const auto copy_cost = num_words(s) * 3; - if ((gas_left -= copy_cost) < 0) + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; if (copy_size > 0) @@ -470,8 +476,7 @@ inline Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) const auto s = static_cast(size); const auto copy_size = std::min(s, code_size - src); - const auto copy_cost = num_words(s) * 3; - if ((gas_left -= copy_cost) < 0) + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; // TODO: Add unit tests for each combination of conditions. @@ -495,6 +500,21 @@ inline void basefee(StackTop stack, ExecutionState& state) noexcept stack.push(intx::be::load(state.get_tx_context().block_base_fee)); } +inline void blobhash(StackTop stack, ExecutionState& state) noexcept +{ + auto& index = stack.top(); + const auto& tx = state.get_tx_context(); + + index = (index < tx.blob_hashes_count) ? + intx::be::load(tx.blob_hashes[static_cast(index)]) : + 0; +} + +inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept +{ + stack.push(intx::be::load(state.get_tx_context().blob_base_fee)); +} + inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); @@ -521,8 +541,7 @@ inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& stat return {EVMC_OUT_OF_GAS, gas_left}; const auto s = static_cast(size); - const auto copy_cost = num_words(s) * 3; - if ((gas_left -= copy_cost) < 0) + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) @@ -544,6 +563,25 @@ inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& stat return {EVMC_SUCCESS, gas_left}; } +inline void returndataload(StackTop stack, ExecutionState& state) noexcept +{ + auto& index = stack.top(); + + if (state.return_data.size() < index) + index = 0; + else + { + const auto begin = static_cast(index); + const auto end = std::min(begin + 32, state.return_data.size()); + + uint8_t data[32] = {}; + for (size_t i = 0; i < (end - begin); ++i) + data[i] = state.return_data[begin + i]; + + index = intx::be::unsafe::load(data); + } +} + inline void returndatasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.return_data.size()); @@ -561,19 +599,36 @@ inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& s auto dst = static_cast(mem_index); auto s = static_cast(size); - if (state.return_data.size() < input_index) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; - auto src = static_cast(input_index); + if (is_eof_container(state.original_code)) + { + auto src = state.return_data.size() < input_index ? state.return_data.size() : + static_cast(input_index); + auto copy_size = std::min(s, state.return_data.size() - src); - if (src + s > state.return_data.size()) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - const auto copy_cost = num_words(s) * 3; - if ((gas_left -= copy_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + if (copy_size > 0) + std::memcpy(&state.memory[dst], &state.return_data[src], copy_size); - if (s > 0) - std::memcpy(&state.memory[dst], &state.return_data[src], s); + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + } + else + { + if (state.return_data.size() < input_index) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + auto src = static_cast(input_index); + + if (src + s > state.return_data.size()) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + + if (s > 0) + std::memcpy(&state.memory[dst], &state.return_data[src], s); + } return {EVMC_SUCCESS, gas_left}; } @@ -686,14 +741,14 @@ Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; /// Internal jump implementation for JUMP/JUMPI instructions. inline code_iterator jump_impl(ExecutionState& state, const uint256& dst) noexcept { - const auto& jumpdest_map = state.analysis.baseline->jumpdest_map; - if (dst >= jumpdest_map.size() || !jumpdest_map[static_cast(dst)]) + const auto hi_part_is_nonzero = (dst[3] | dst[2] | dst[1]) != 0; + if (hi_part_is_nonzero || !state.analysis.baseline->check_jumpdest(dst[0])) [[unlikely]] { state.status = EVMC_BAD_JUMP_DESTINATION; return nullptr; } - return &state.analysis.baseline->executable_code[static_cast(dst)]; + return &state.analysis.baseline->executable_code()[static_cast(dst[0])]; } /// JUMP instruction implementation using baseline::CodeAnalysis. @@ -728,10 +783,10 @@ inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iter constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); const auto case_ = stack.pop(); - const auto count = pc[1]; - const auto pc_post = pc + 1 + 1 /* count */ + count * REL_OFFSET_SIZE /* tbl */; + const auto max_index = pc[1]; + const auto pc_post = pc + 1 + 1 /* max_index */ + (max_index + 1) * REL_OFFSET_SIZE /* tbl */; - if (case_ >= count) + if (case_ > max_index) { return pc_post; } @@ -746,7 +801,7 @@ inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iter inline code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { - stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); + stack.push(static_cast(pos - state.analysis.baseline->executable_code().data())); return pos + 1; } @@ -761,6 +816,25 @@ inline Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) n return {EVMC_SUCCESS, gas_left}; } +inline void tload(StackTop stack, ExecutionState& state) noexcept +{ + auto& x = stack.top(); + const auto key = intx::be::store(x); + const auto value = state.host.get_transient_storage(state.msg->recipient, key); + x = intx::be::load(value); +} + +inline Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept +{ + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, 0}; + + const auto key = intx::be::store(stack.pop()); + const auto value = intx::be::store(stack.pop()); + state.host.set_transient_storage(state.msg->recipient, key, value); + return {EVMC_SUCCESS, gas_left}; +} + inline void push0(StackTop stack) noexcept { stack.push({}); @@ -865,39 +939,109 @@ inline void swap(StackTop stack) noexcept a[3] = t3; } -inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +inline code_iterator dupn(StackTop stack, code_iterator pos) noexcept { - const auto n = pos[1] + 1; + stack.push(stack[pos[1]]); + return pos + 2; +} - const auto stack_size = &stack.top() - state.stack_space.bottom(); +inline code_iterator swapn(StackTop stack, code_iterator pos) noexcept +{ + // TODO: This may not be optimal, see instr::core::swap(). + std::swap(stack.top(), stack[pos[1] + 1]); + return pos + 2; +} - if (stack_size < n) +inline code_iterator exchange(StackTop stack, code_iterator pos) noexcept +{ + const auto n = (pos[1] >> 4) + 1; + const auto m = (pos[1] & 0x0f) + 1; + // TODO: This may not be optimal, see instr::core::swap(). + std::swap(stack[n], stack[n + m]); + return pos + 2; +} + +inline Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept +{ + const auto& dst_u256 = stack.pop(); + const auto& src_u256 = stack.pop(); + const auto& size_u256 = stack.pop(); + + if (!check_memory(gas_left, state.memory, std::max(dst_u256, src_u256), size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto dst = static_cast(dst_u256); + const auto src = static_cast(src_u256); + const auto size = static_cast(size_u256); + + if (const auto cost = copy_cost(size); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + + if (size > 0) + std::memmove(&state.memory[dst], &state.memory[src], size); + + return {EVMC_SUCCESS, gas_left}; +} + +inline void dataload(StackTop stack, ExecutionState& state) noexcept +{ + const auto data = state.analysis.baseline->eof_data(); + auto& index = stack.top(); + + if (data.size() < index) + index = 0; + else { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; + const auto begin = static_cast(index); + const auto end = std::min(begin + 32, data.size()); + + uint8_t d[32] = {}; + for (size_t i = 0; i < (end - begin); ++i) + d[i] = data[begin + i]; + + index = intx::be::unsafe::load(d); } +} - stack.push(stack[n - 1]); +inline void datasize(StackTop stack, ExecutionState& state) noexcept +{ + stack.push(state.analysis.baseline->eof_data().size()); +} - return pos + 2; +inline code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +{ + const auto index = read_uint16_be(&pos[1]); + + stack.push(intx::be::unsafe::load(&state.analysis.baseline->eof_data()[index])); + return pos + 3; } -inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +inline Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - const auto n = pos[1] + 1; + const auto data = state.analysis.baseline->eof_data(); + const auto& mem_index = stack.pop(); + const auto& data_index = stack.pop(); + const auto& size = stack.pop(); - const auto stack_size = &stack.top() - state.stack_space.bottom(); + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - if (stack_size <= n) - { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; - } + const auto dst = static_cast(mem_index); + // TODO why? + const auto src = data.size() < data_index ? data.size() : static_cast(data_index); + const auto s = static_cast(size); + const auto copy_size = std::min(s, data.size() - src); - // TODO: This may not be optimal, see instr::core::swap(). - std::swap(stack.top(), stack[n]); + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - return pos + 2; + if (copy_size > 0) + std::memcpy(&state.memory[dst], &data[src], copy_size); + + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + + return {EVMC_SUCCESS, gas_left}; } template @@ -938,17 +1082,29 @@ inline constexpr auto callcode = call_impl; inline constexpr auto delegatecall = call_impl; inline constexpr auto staticcall = call_impl; +template +Result extcall_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +inline constexpr auto extcall = extcall_impl; +inline constexpr auto extdelegatecall = extcall_impl; +inline constexpr auto extstaticcall = extcall_impl; + template Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; inline constexpr auto create = create_impl; inline constexpr auto create2 = create_impl; +Result eofcreate( + StackTop stack, int64_t gas_left, ExecutionState& state, code_iterator& pos) noexcept; + inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto index = read_uint16_be(&pos[1]); - const auto& header = state.analysis.baseline->eof_header; + const auto& header = state.analysis.baseline->eof_header(); const auto stack_size = &stack.top() - state.stack_space.bottom(); - if (stack_size + header.types[index].max_stack_height > StackSpace::limit) + + const auto callee_required_stack_size = + header.types[index].max_stack_height - header.types[index].inputs; + if (stack_size + callee_required_stack_size > StackSpace::limit) { state.status = EVMC_STACK_OVERFLOW; return nullptr; @@ -963,8 +1119,7 @@ inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator state.call_stack.push_back(pos + 3); const auto offset = header.code_offsets[index] - header.code_offsets[0]; - auto code = state.analysis.baseline->executable_code; - return code.data() + offset; + return state.analysis.baseline->executable_code().data() + offset; } inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept @@ -974,6 +1129,24 @@ inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterat return p; } +inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +{ + const auto index = read_uint16_be(&pos[1]); + const auto& header = state.analysis.baseline->eof_header(); + const auto stack_size = &stack.top() - state.stack_space.bottom(); + + const auto callee_required_stack_size = + header.types[index].max_stack_height - header.types[index].inputs; + if (stack_size + callee_required_stack_size > StackSpace::limit) + { + state.status = EVMC_STACK_OVERFLOW; + return nullptr; + } + + const auto offset = header.code_offsets[index] - header.code_offsets[0]; + return state.analysis.baseline->executable_code().data() + offset; +} + template inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { @@ -991,6 +1164,29 @@ inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& inline constexpr auto return_ = return_impl; inline constexpr auto revert = return_impl; +inline TermResult returncontract( + StackTop stack, int64_t gas_left, ExecutionState& state, code_iterator pos) noexcept +{ + const auto& offset = stack[0]; + const auto& size = stack[1]; + + if (!check_memory(gas_left, state.memory, offset, size)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto deploy_container_index = size_t{pos[1]}; + bytes deploy_container{state.analysis.baseline->eof_header().get_container( + state.original_code, deploy_container_index)}; + + // Append (offset, size) to data section + if (!append_data_section(deploy_container, + {&state.memory[static_cast(offset)], static_cast(size)})) + return {EVMC_OUT_OF_GAS, gas_left}; + + state.deploy_container = std::move(deploy_container); + + return {EVMC_SUCCESS, gas_left}; +} + inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { if (state.in_static_mode()) diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index 8338e8bf..3632c2a9 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -2,10 +2,46 @@ // Copyright 2019 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include "eof.hpp" #include "instructions.hpp" +constexpr int64_t MIN_RETAINED_GAS = 5000; +constexpr int64_t MIN_CALLEE_GAS = 2300; +constexpr int64_t CALL_VALUE_COST = 9000; +constexpr int64_t ACCOUNT_CREATION_COST = 25000; + +constexpr auto EXTCALL_SUCCESS = 0; +constexpr auto EXTCALL_REVERT = 1; +constexpr auto EXTCALL_ABORT = 2; + namespace evmone::instr::core { +/// Converts an opcode to matching EVMC call kind. +consteval evmc_call_kind to_call_kind(Opcode op) noexcept +{ + switch (op) + { + case OP_CALL: + case OP_EXTCALL: + case OP_STATICCALL: + case OP_EXTSTATICCALL: + return EVMC_CALL; + case OP_CALLCODE: + return EVMC_CALLCODE; + case OP_DELEGATECALL: + case OP_EXTDELEGATECALL: + return EVMC_DELEGATECALL; + case OP_CREATE: + return EVMC_CREATE; + case OP_CREATE2: + return EVMC_CREATE2; + case OP_EOFCREATE: + return EVMC_EOFCREATE; + default: + intx::unreachable(); + } +} + template Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { @@ -29,6 +65,7 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce if ((gas_left -= instr::additional_cold_account_access_cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; } + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) return {EVMC_OUT_OF_GAS, gas_left}; @@ -40,10 +77,7 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce const auto output_offset = static_cast(output_offset_u256); const auto output_size = static_cast(output_size_u256); - auto msg = evmc_message{}; - msg.kind = (Op == OP_DELEGATECALL) ? EVMC_DELEGATECALL : - (Op == OP_CALLCODE) ? EVMC_CALLCODE : - EVMC_CALL; + evmc_message msg{.kind = to_call_kind(Op)}; msg.flags = (Op == OP_STATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; msg.depth = state.msg->depth + 1; msg.recipient = (Op == OP_CALL || Op == OP_STATICCALL) ? dst : state.msg->recipient; @@ -58,6 +92,7 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce msg.input_data = &state.memory[input_offset]; msg.input_size = input_size; } + int64_t cost = has_value ? 2300 : 0; if(has_value) { @@ -80,12 +115,14 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce } else if( state.eos_evm_version >= 1 ) { cost += static_cast(state.gas_params.G_newaccount); } else { - cost += 25000; + cost += ACCOUNT_CREATION_COST; } } } + if ((gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; + msg.gas = std::numeric_limits::max(); if (gas < msg.gas) msg.gas = static_cast(gas); @@ -104,12 +141,10 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce if (state.msg->depth >= 1024) return {EVMC_SUCCESS, gas_left}; // "Light" failure. - if (has_value && intx::be::load(state.host.get_balance(state.msg->recipient)) < value) { + if (has_value && intx::be::load(state.host.get_balance(state.msg->recipient)) < value) return {EVMC_SUCCESS, gas_left}; // "Light" failure. - } const auto result = state.host.call(msg); - state.return_data.assign(result.output_data, result.output_size); stack.top() = result.status_code == EVMC_SUCCESS; @@ -137,6 +172,136 @@ template Result call_impl( template Result call_impl( StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template +Result extcall_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept +{ + static_assert(Op == OP_EXTCALL || Op == OP_EXTDELEGATECALL || Op == OP_EXTSTATICCALL); + + const auto dst_u256 = stack.pop(); + const auto input_offset_u256 = stack.pop(); + const auto input_size_u256 = stack.pop(); + const auto value = (Op == OP_EXTSTATICCALL || Op == OP_EXTDELEGATECALL) ? 0 : stack.pop(); + const auto has_value = value != 0; + + stack.push(EXTCALL_ABORT); // Assume (hard) failure. + state.return_data.clear(); + + // Address space expansion ready check. + static constexpr auto ADDRESS_MAX = (uint256{1} << 160) - 1; + if (dst_u256 > ADDRESS_MAX) + return {EVMC_ARGUMENT_OUT_OF_RANGE, gas_left}; + + const auto dst = intx::be::trunc(dst_u256); + + if (state.host.access_account(dst) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } + + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto input_offset = static_cast(input_offset_u256); + const auto input_size = static_cast(input_size_u256); + + evmc_message msg{.kind = to_call_kind(Op)}; + msg.flags = (Op == OP_EXTSTATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; + msg.depth = state.msg->depth + 1; + msg.recipient = (Op != OP_EXTDELEGATECALL) ? dst : state.msg->recipient; + msg.code_address = dst; + msg.sender = (Op == OP_EXTDELEGATECALL) ? state.msg->sender : state.msg->recipient; + msg.value = + (Op == OP_EXTDELEGATECALL) ? state.msg->value : intx::be::store(value); + + if (input_size > 0) + { + // input_offset may be garbage if input_size == 0. + msg.input_data = &state.memory[input_offset]; + msg.input_size = input_size; + } + + int64_t cost = has_value ? 2300 : 0; + + if(has_value) { + if(state.eos_evm_version >= 3) { + cost += state.gas_state.apply_speculative_cpu_gas_delta(6700); + } else { + cost += 6700; + } + } + + if constexpr (Op == OP_EXTCALL) + { + if (has_value && state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + + if (has_value && !state.host.account_exists(dst)){ + if( state.eos_evm_version >= 3 ) { + auto storage_cost = state.gas_state.apply_storage_gas_delta(static_cast(state.gas_params.G_newaccount)); + cost += storage_cost; + } else if( state.eos_evm_version >= 1 ) { + cost += static_cast(state.gas_params.G_newaccount); + } else { + cost += ACCOUNT_CREATION_COST; + } + } + } + + if ((gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + + msg.gas = gas_left - std::max(gas_left / 64, MIN_RETAINED_GAS); + + if (msg.gas < MIN_CALLEE_GAS || state.msg->depth >= 1024 || + (has_value && + intx::be::load(state.host.get_balance(state.msg->recipient)) < value)) + { + stack.top() = EXTCALL_REVERT; + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + } + + if constexpr (Op == OP_EXTDELEGATECALL) + { + // The code targeted by EXTDELEGATECALL must also be an EOF. + // This restriction has been added to EIP-3540 in + // https://github.com/ethereum/EIPs/pull/7131 + uint8_t target_code_prefix[2]; + const auto s = state.host.copy_code( + msg.code_address, 0, target_code_prefix, std::size(target_code_prefix)); + if (!is_eof_container({target_code_prefix, s})) + { + stack.top() = EXTCALL_REVERT; + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + } + } + + const auto result = state.host.call(msg); + state.return_data.assign(result.output_data, result.output_size); + if (result.status_code == EVMC_SUCCESS) + stack.top() = EXTCALL_SUCCESS; + else if (result.status_code == EVMC_REVERT) + stack.top() = EXTCALL_REVERT; + else + stack.top() = EXTCALL_ABORT; + + if( state.eos_evm_version >= 3 ) { + gas_left -= state.gas_state.integrate(msg.gas - result.gas_left, + gas_state_t::from_result(state.eos_evm_version, result)); + } else { + const auto gas_used = msg.gas - result.gas_left; + gas_left -= gas_used; + state.gas_state.add_cpu_gas_refund(result.gas_refund); + } + return {EVMC_SUCCESS, gas_left}; +} + +template Result extcall_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result extcall_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result extcall_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; template Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept @@ -186,22 +351,100 @@ Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noex intx::be::load(state.host.get_balance(state.msg->recipient)) < endowment) return {EVMC_SUCCESS, gas_left}; // "Light" failure. - auto msg = evmc_message{}; + evmc_message msg{.kind = to_call_kind(Op)}; msg.gas = gas_left; if (state.rev >= EVMC_TANGERINE_WHISTLE) msg.gas = msg.gas - msg.gas / 64; - msg.kind = (Op == OP_CREATE) ? EVMC_CREATE : EVMC_CREATE2; if (init_code_size > 0) { // init_code_offset may be garbage if init_code_size == 0. msg.input_data = &state.memory[init_code_offset]; msg.input_size = init_code_size; + + if (state.rev >= EVMC_PRAGUE) + { + // EOF initcode is not allowed for legacy creation + if (is_eof_container({msg.input_data, msg.input_size})) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + } + } + msg.sender = state.msg->recipient; + msg.depth = state.msg->depth + 1; + msg.create2_salt = intx::be::store(salt); + msg.value = intx::be::store(endowment); + + const auto result = state.host.call(msg); + if( state.eos_evm_version >= 3 ) { + gas_left -= state.gas_state.integrate(msg.gas - result.gas_left, + gas_state_t::from_result(state.eos_evm_version, result)); + } else { + gas_left -= msg.gas - result.gas_left; + state.gas_state.add_cpu_gas_refund(result.gas_refund); + } + + state.return_data.assign(result.output_data, result.output_size); + if (result.status_code == EVMC_SUCCESS) + stack.top() = intx::be::load(result.create_address); + + return {EVMC_SUCCESS, gas_left}; +} + +Result eofcreate( + StackTop stack, int64_t gas_left, ExecutionState& state, code_iterator& pos) noexcept +{ + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + + const auto endowment = stack.pop(); + const auto salt = stack.pop(); + const auto input_offset_u256 = stack.pop(); + const auto input_size_u256 = stack.pop(); + + stack.push(0); // Assume failure. + state.return_data.clear(); + + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto initcontainer_index = pos[1]; + pos += 2; + const auto& container = state.original_code; + const auto& eof_header = state.analysis.baseline->eof_header(); + const auto initcontainer = eof_header.get_container(container, initcontainer_index); + + // Charge for initcode hashing. + constexpr auto initcode_word_cost_hashing = 6; + const auto initcode_cost_hashing = num_words(initcontainer.size()) * initcode_word_cost_hashing; + if ((gas_left -= initcode_cost_hashing) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto input_offset = static_cast(input_offset_u256); + const auto input_size = static_cast(input_size_u256); + + if (state.msg->depth >= 1024) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + + if (endowment != 0 && + intx::be::load(state.host.get_balance(state.msg->recipient)) < endowment) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + + evmc_message msg{.kind = EVMC_EOFCREATE}; + msg.gas = gas_left - gas_left / 64; + if (input_size > 0) + { + // input_data may be garbage if init_code_size == 0. + msg.input_data = &state.memory[input_offset]; + msg.input_size = input_size; } + msg.sender = state.msg->recipient; msg.depth = state.msg->depth + 1; msg.create2_salt = intx::be::store(salt); msg.value = intx::be::store(endowment); + // init_code is guaranteed to be non-empty by validation of container sections + msg.code = initcontainer.data(); + msg.code_size = initcontainer.size(); const auto result = state.host.call(msg); if( state.eos_evm_version >= 3 ) { @@ -211,6 +454,7 @@ Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noex gas_left -= msg.gas - result.gas_left; state.gas_state.add_cpu_gas_refund(result.gas_refund); } + state.return_data.assign(result.output_data, result.output_size); if (result.status_code == EVMC_SUCCESS) stack.top() = intx::be::load(result.create_address); diff --git a/lib/evmone/instructions_opcodes.hpp b/lib/evmone/instructions_opcodes.hpp index 9b430ce4..66297326 100644 --- a/lib/evmone/instructions_opcodes.hpp +++ b/lib/evmone/instructions_opcodes.hpp @@ -69,6 +69,8 @@ enum Opcode : uint8_t OP_CHAINID = 0x46, OP_SELFBALANCE = 0x47, OP_BASEFEE = 0x48, + OP_BLOBHASH = 0x49, + OP_BLOBBASEFEE = 0x4a, OP_POP = 0x50, OP_MLOAD = 0x51, @@ -82,10 +84,9 @@ enum Opcode : uint8_t OP_MSIZE = 0x59, OP_GAS = 0x5a, OP_JUMPDEST = 0x5b, - OP_RJUMP = 0x5c, - OP_RJUMPI = 0x5d, - OP_RJUMPV = 0x5e, - + OP_TLOAD = 0x5c, + OP_TSTORE = 0x5d, + OP_MCOPY = 0x5e, OP_PUSH0 = 0x5f, OP_PUSH1 = 0x60, OP_PUSH2 = 0x61, @@ -157,11 +158,24 @@ enum Opcode : uint8_t OP_LOG3 = 0xa3, OP_LOG4 = 0xa4, - OP_CALLF = 0xb0, - OP_RETF = 0xb1, + OP_DATALOAD = 0xd0, + OP_DATALOADN = 0xd1, + OP_DATASIZE = 0xd2, + OP_DATACOPY = 0xd3, + + OP_RJUMP = 0xe0, + OP_RJUMPI = 0xe1, + OP_RJUMPV = 0xe2, + OP_CALLF = 0xe3, + OP_RETF = 0xe4, + OP_JUMPF = 0xe5, + + OP_DUPN = 0xe6, + OP_SWAPN = 0xe7, + OP_EXCHANGE = 0xe8, - OP_DUPN = 0xb5, - OP_SWAPN = 0xb6, + OP_EOFCREATE = 0xec, + OP_RETURNCONTRACT = 0xee, OP_CREATE = 0xf0, OP_CALL = 0xf1, @@ -169,8 +183,12 @@ enum Opcode : uint8_t OP_RETURN = 0xf3, OP_DELEGATECALL = 0xf4, OP_CREATE2 = 0xf5, + OP_RETURNDATALOAD = 0xf7, + OP_EXTCALL = 0xf8, + OP_EXTDELEGATECALL = 0xf9, OP_STATICCALL = 0xfa, + OP_EXTSTATICCALL = 0xfb, OP_REVERT = 0xfd, OP_INVALID = 0xfe, diff --git a/lib/evmone/instructions_storage.cpp b/lib/evmone/instructions_storage.cpp index 5b578aca..0c335cd4 100644 --- a/lib/evmone/instructions_storage.cpp +++ b/lib/evmone/instructions_storage.cpp @@ -39,6 +39,8 @@ constexpr auto storage_cost_spec = []() noexcept { tbl[EVMC_PARIS] = tbl[EVMC_LONDON]; tbl[EVMC_SHANGHAI] = tbl[EVMC_LONDON]; tbl[EVMC_CANCUN] = tbl[EVMC_LONDON]; + tbl[EVMC_PRAGUE] = tbl[EVMC_LONDON]; + tbl[EVMC_OSAKA] = tbl[EVMC_LONDON]; return tbl; }(); diff --git a/lib/evmone/instructions_traits.hpp b/lib/evmone/instructions_traits.hpp index 860a3875..68af9c8d 100644 --- a/lib/evmone/instructions_traits.hpp +++ b/lib/evmone/instructions_traits.hpp @@ -164,21 +164,43 @@ constexpr inline GasCostTable gas_costs = []() noexcept { table[EVMC_SHANGHAI][OP_PUSH0] = 2; table[EVMC_CANCUN] = table[EVMC_SHANGHAI]; - table[EVMC_CANCUN][OP_DUPN] = 3; - table[EVMC_CANCUN][OP_SWAPN] = 3; - table[EVMC_CANCUN][OP_RJUMP] = 2; - table[EVMC_CANCUN][OP_RJUMPI] = 4; - table[EVMC_CANCUN][OP_RJUMPV] = 4; - table[EVMC_CANCUN][OP_CALLF] = 5; - table[EVMC_CANCUN][OP_RETF] = 3; + table[EVMC_CANCUN][OP_BLOBHASH] = 3; + table[EVMC_CANCUN][OP_BLOBBASEFEE] = 2; + table[EVMC_CANCUN][OP_TLOAD] = warm_storage_read_cost; + table[EVMC_CANCUN][OP_TSTORE] = warm_storage_read_cost; + table[EVMC_CANCUN][OP_MCOPY] = 3; table[EVMC_PRAGUE] = table[EVMC_CANCUN]; + table[EVMC_PRAGUE][OP_DUPN] = 3; + table[EVMC_PRAGUE][OP_SWAPN] = 3; + table[EVMC_PRAGUE][OP_EXCHANGE] = 3; + table[EVMC_PRAGUE][OP_RJUMP] = 2; + table[EVMC_PRAGUE][OP_RJUMPI] = 4; + table[EVMC_PRAGUE][OP_RJUMPV] = 4; + table[EVMC_PRAGUE][OP_CALLF] = 5; + table[EVMC_PRAGUE][OP_RETF] = 3; + table[EVMC_PRAGUE][OP_JUMPF] = 5; + table[EVMC_PRAGUE][OP_DATALOAD] = 4; + table[EVMC_PRAGUE][OP_DATALOADN] = 3; + table[EVMC_PRAGUE][OP_DATASIZE] = 2; + table[EVMC_PRAGUE][OP_DATACOPY] = 3; + table[EVMC_PRAGUE][OP_RETURNDATALOAD] = 3; + table[EVMC_PRAGUE][OP_EXTCALL] = warm_storage_read_cost; + table[EVMC_PRAGUE][OP_EXTDELEGATECALL] = warm_storage_read_cost; + table[EVMC_PRAGUE][OP_EXTSTATICCALL] = warm_storage_read_cost; + table[EVMC_PRAGUE][OP_EOFCREATE] = 32000; + table[EVMC_PRAGUE][OP_RETURNCONTRACT] = 0; + + table[EVMC_OSAKA] = table[EVMC_PRAGUE]; return table; }(); static_assert(gas_costs[EVMC_MAX_REVISION][OP_ADD] > 0, "gas costs missing for a revision"); +/// The revision related to introduction of the EOFv1. +constexpr auto REV_EOF1 = EVMC_PRAGUE; + /// The EVM instruction traits. struct Traits @@ -194,7 +216,7 @@ struct Traits bool is_terminating = false; /// The number of stack items the instruction accesses during execution. - int8_t stack_height_required = 0; + uint8_t stack_height_required = 0; /// The stack height change caused by the instruction execution. Can be negative. int8_t stack_height_change = 0; @@ -203,12 +225,16 @@ struct Traits /// every EVM revision the value is ::EVMC_FRONTIER. For undefined instructions the value is not /// available. std::optional since; + + /// The EVM revision in which the instruction has become valid in EOF. For instructions invalid + /// in EOF the value is not available. + std::optional eof_since; }; /// Determines if an instruction has constant base gas cost across all revisions. /// Note that this is not true for instructions with constant base gas cost but /// not available in the first revision (e.g. SHL). -inline constexpr bool has_const_gas_cost(Opcode op) noexcept +consteval bool has_const_gas_cost(Opcode op) noexcept { const auto g = gas_costs[EVMC_FRONTIER][op]; for (size_t r = EVMC_FRONTIER + 1; r <= EVMC_MAX_REVISION; ++r) @@ -224,169 +250,186 @@ inline constexpr bool has_const_gas_cost(Opcode op) noexcept constexpr inline std::array traits = []() noexcept { std::array table{}; - table[OP_STOP] = {"STOP", 0, true, 0, 0, EVMC_FRONTIER}; - table[OP_ADD] = {"ADD", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_MUL] = {"MUL", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SUB] = {"SUB", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_DIV] = {"DIV", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SDIV] = {"SDIV", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_MOD] = {"MOD", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SMOD] = {"SMOD", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_ADDMOD] = {"ADDMOD", 0, false, 3, -2, EVMC_FRONTIER}; - table[OP_MULMOD] = {"MULMOD", 0, false, 3, -2, EVMC_FRONTIER}; - table[OP_EXP] = {"EXP", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SIGNEXTEND] = {"SIGNEXTEND", 0, false, 2, -1, EVMC_FRONTIER}; - - table[OP_LT] = {"LT", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_GT] = {"GT", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SLT] = {"SLT", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SGT] = {"SGT", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_EQ] = {"EQ", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_ISZERO] = {"ISZERO", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_AND] = {"AND", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_OR] = {"OR", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_XOR] = {"XOR", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_NOT] = {"NOT", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_BYTE] = {"BYTE", 0, false, 2, -1, EVMC_FRONTIER}; - table[OP_SHL] = {"SHL", 0, false, 2, -1, EVMC_CONSTANTINOPLE}; - table[OP_SHR] = {"SHR", 0, false, 2, -1, EVMC_CONSTANTINOPLE}; - table[OP_SAR] = {"SAR", 0, false, 2, -1, EVMC_CONSTANTINOPLE}; - - table[OP_KECCAK256] = {"KECCAK256", 0, false, 2, -1, EVMC_FRONTIER}; - - table[OP_ADDRESS] = {"ADDRESS", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_BALANCE] = {"BALANCE", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_ORIGIN] = {"ORIGIN", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_CALLER] = {"CALLER", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_CALLVALUE] = {"CALLVALUE", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_CALLDATALOAD] = {"CALLDATALOAD", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_CALLDATASIZE] = {"CALLDATASIZE", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_CALLDATACOPY] = {"CALLDATACOPY", 0, false, 3, -3, EVMC_FRONTIER}; + table[OP_STOP] = {"STOP", 0, true, 0, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_ADD] = {"ADD", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_MUL] = {"MUL", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SUB] = {"SUB", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DIV] = {"DIV", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SDIV] = {"SDIV", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_MOD] = {"MOD", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SMOD] = {"SMOD", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_ADDMOD] = {"ADDMOD", 0, false, 3, -2, EVMC_FRONTIER, REV_EOF1}; + table[OP_MULMOD] = {"MULMOD", 0, false, 3, -2, EVMC_FRONTIER, REV_EOF1}; + table[OP_EXP] = {"EXP", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SIGNEXTEND] = {"SIGNEXTEND", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + + table[OP_LT] = {"LT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_GT] = {"GT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SLT] = {"SLT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SGT] = {"SGT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_EQ] = {"EQ", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_ISZERO] = {"ISZERO", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_AND] = {"AND", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_OR] = {"OR", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_XOR] = {"XOR", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_NOT] = {"NOT", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_BYTE] = {"BYTE", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_SHL] = {"SHL", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1}; + table[OP_SHR] = {"SHR", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1}; + table[OP_SAR] = {"SAR", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1}; + + table[OP_KECCAK256] = {"KECCAK256", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1}; + + table[OP_ADDRESS] = {"ADDRESS", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_BALANCE] = {"BALANCE", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_ORIGIN] = {"ORIGIN", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_CALLER] = {"CALLER", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_CALLVALUE] = {"CALLVALUE", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_CALLDATALOAD] = {"CALLDATALOAD", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_CALLDATASIZE] = {"CALLDATASIZE", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_CALLDATACOPY] = {"CALLDATACOPY", 0, false, 3, -3, EVMC_FRONTIER, REV_EOF1}; table[OP_CODESIZE] = {"CODESIZE", 0, false, 0, 1, EVMC_FRONTIER}; table[OP_CODECOPY] = {"CODECOPY", 0, false, 3, -3, EVMC_FRONTIER}; - table[OP_GASPRICE] = {"GASPRICE", 0, false, 0, 1, EVMC_FRONTIER}; + table[OP_GASPRICE] = {"GASPRICE", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; table[OP_EXTCODESIZE] = {"EXTCODESIZE", 0, false, 1, 0, EVMC_FRONTIER}; table[OP_EXTCODECOPY] = {"EXTCODECOPY", 0, false, 4, -4, EVMC_FRONTIER}; - table[OP_RETURNDATASIZE] = {"RETURNDATASIZE", 0, false, 0, 1, EVMC_BYZANTIUM}; - table[OP_RETURNDATACOPY] = {"RETURNDATACOPY", 0, false, 3, -3, EVMC_BYZANTIUM}; + table[OP_RETURNDATASIZE] = {"RETURNDATASIZE", 0, false, 0, 1, EVMC_BYZANTIUM, REV_EOF1}; + table[OP_RETURNDATACOPY] = {"RETURNDATACOPY", 0, false, 3, -3, EVMC_BYZANTIUM, REV_EOF1}; table[OP_EXTCODEHASH] = {"EXTCODEHASH", 0, false, 1, 0, EVMC_CONSTANTINOPLE}; - table[OP_BLOCKHASH] = {"BLOCKHASH", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_COINBASE] = {"COINBASE", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_TIMESTAMP] = {"TIMESTAMP", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_NUMBER] = {"NUMBER", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_PREVRANDAO] = {"PREVRANDAO", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_GASLIMIT] = {"GASLIMIT", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_CHAINID] = {"CHAINID", 0, false, 0, 1, EVMC_ISTANBUL}; - table[OP_SELFBALANCE] = {"SELFBALANCE", 0, false, 0, 1, EVMC_ISTANBUL}; - table[OP_BASEFEE] = {"BASEFEE", 0, false, 0, 1, EVMC_LONDON}; - - table[OP_POP] = {"POP", 0, false, 1, -1, EVMC_FRONTIER}; - table[OP_MLOAD] = {"MLOAD", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_MSTORE] = {"MSTORE", 0, false, 2, -2, EVMC_FRONTIER}; - table[OP_MSTORE8] = {"MSTORE8", 0, false, 2, -2, EVMC_FRONTIER}; - table[OP_SLOAD] = {"SLOAD", 0, false, 1, 0, EVMC_FRONTIER}; - table[OP_SSTORE] = {"SSTORE", 0, false, 2, -2, EVMC_FRONTIER}; + table[OP_BLOCKHASH] = {"BLOCKHASH", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_COINBASE] = {"COINBASE", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_TIMESTAMP] = {"TIMESTAMP", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_NUMBER] = {"NUMBER", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PREVRANDAO] = {"PREVRANDAO", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_GASLIMIT] = {"GASLIMIT", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_CHAINID] = {"CHAINID", 0, false, 0, 1, EVMC_ISTANBUL, REV_EOF1}; + table[OP_SELFBALANCE] = {"SELFBALANCE", 0, false, 0, 1, EVMC_ISTANBUL, REV_EOF1}; + table[OP_BASEFEE] = {"BASEFEE", 0, false, 0, 1, EVMC_LONDON, REV_EOF1}; + table[OP_BLOBHASH] = {"BLOBHASH", 0, false, 1, 0, EVMC_CANCUN, REV_EOF1}; + table[OP_BLOBBASEFEE] = {"BLOBBASEFEE", 0, false, 0, 1, EVMC_CANCUN, REV_EOF1}; + + table[OP_POP] = {"POP", 0, false, 1, -1, EVMC_FRONTIER, REV_EOF1}; + table[OP_MLOAD] = {"MLOAD", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_MSTORE] = {"MSTORE", 0, false, 2, -2, EVMC_FRONTIER, REV_EOF1}; + table[OP_MSTORE8] = {"MSTORE8", 0, false, 2, -2, EVMC_FRONTIER, REV_EOF1}; + table[OP_SLOAD] = {"SLOAD", 0, false, 1, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SSTORE] = {"SSTORE", 0, false, 2, -2, EVMC_FRONTIER, REV_EOF1}; table[OP_JUMP] = {"JUMP", 0, false, 1, -1, EVMC_FRONTIER}; table[OP_JUMPI] = {"JUMPI", 0, false, 2, -2, EVMC_FRONTIER}; table[OP_PC] = {"PC", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_MSIZE] = {"MSIZE", 0, false, 0, 1, EVMC_FRONTIER}; + table[OP_MSIZE] = {"MSIZE", 0, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; table[OP_GAS] = {"GAS", 0, false, 0, 1, EVMC_FRONTIER}; - table[OP_JUMPDEST] = {"JUMPDEST", 0, false, 0, 0, EVMC_FRONTIER}; - table[OP_RJUMP] = {"RJUMP", 2, false, 0, 0, EVMC_CANCUN}; - table[OP_RJUMPI] = {"RJUMPI", 2, false, 1, -1, EVMC_CANCUN}; + table[OP_JUMPDEST] = {"JUMPDEST", 0, false, 0, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_RJUMP] = {"RJUMP", 2, false, 0, 0, {}, REV_EOF1}; + table[OP_RJUMPI] = {"RJUMPI", 2, false, 1, -1, {}, REV_EOF1}; table[OP_RJUMPV] = { - "RJUMPV", 0 /* WARNING: immediate_size is dynamic */, false, 1, -1, EVMC_CANCUN}; - - table[OP_PUSH0] = {"PUSH0", 0, false, 0, 1, EVMC_SHANGHAI}; - - table[OP_PUSH1] = {"PUSH1", 1, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH2] = {"PUSH2", 2, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH3] = {"PUSH3", 3, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH4] = {"PUSH4", 4, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH5] = {"PUSH5", 5, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH6] = {"PUSH6", 6, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH7] = {"PUSH7", 7, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH8] = {"PUSH8", 8, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH9] = {"PUSH9", 9, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH10] = {"PUSH10", 10, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH11] = {"PUSH11", 11, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH12] = {"PUSH12", 12, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH13] = {"PUSH13", 13, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH14] = {"PUSH14", 14, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH15] = {"PUSH15", 15, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH16] = {"PUSH16", 16, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH17] = {"PUSH17", 17, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH18] = {"PUSH18", 18, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH19] = {"PUSH19", 19, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH20] = {"PUSH20", 20, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH21] = {"PUSH21", 21, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH22] = {"PUSH22", 22, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH23] = {"PUSH23", 23, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH24] = {"PUSH24", 24, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH25] = {"PUSH25", 25, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH26] = {"PUSH26", 26, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH27] = {"PUSH27", 27, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH28] = {"PUSH28", 28, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH29] = {"PUSH29", 29, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH30] = {"PUSH30", 30, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH31] = {"PUSH31", 31, false, 0, 1, EVMC_FRONTIER}; - table[OP_PUSH32] = {"PUSH32", 32, false, 0, 1, EVMC_FRONTIER}; - - table[OP_DUP1] = {"DUP1", 0, false, 1, 1, EVMC_FRONTIER}; - table[OP_DUP2] = {"DUP2", 0, false, 2, 1, EVMC_FRONTIER}; - table[OP_DUP3] = {"DUP3", 0, false, 3, 1, EVMC_FRONTIER}; - table[OP_DUP4] = {"DUP4", 0, false, 4, 1, EVMC_FRONTIER}; - table[OP_DUP5] = {"DUP5", 0, false, 5, 1, EVMC_FRONTIER}; - table[OP_DUP6] = {"DUP6", 0, false, 6, 1, EVMC_FRONTIER}; - table[OP_DUP7] = {"DUP7", 0, false, 7, 1, EVMC_FRONTIER}; - table[OP_DUP8] = {"DUP8", 0, false, 8, 1, EVMC_FRONTIER}; - table[OP_DUP9] = {"DUP9", 0, false, 9, 1, EVMC_FRONTIER}; - table[OP_DUP10] = {"DUP10", 0, false, 10, 1, EVMC_FRONTIER}; - table[OP_DUP11] = {"DUP11", 0, false, 11, 1, EVMC_FRONTIER}; - table[OP_DUP12] = {"DUP12", 0, false, 12, 1, EVMC_FRONTIER}; - table[OP_DUP13] = {"DUP13", 0, false, 13, 1, EVMC_FRONTIER}; - table[OP_DUP14] = {"DUP14", 0, false, 14, 1, EVMC_FRONTIER}; - table[OP_DUP15] = {"DUP15", 0, false, 15, 1, EVMC_FRONTIER}; - table[OP_DUP16] = {"DUP16", 0, false, 16, 1, EVMC_FRONTIER}; - - table[OP_SWAP1] = {"SWAP1", 0, false, 2, 0, EVMC_FRONTIER}; - table[OP_SWAP2] = {"SWAP2", 0, false, 3, 0, EVMC_FRONTIER}; - table[OP_SWAP3] = {"SWAP3", 0, false, 4, 0, EVMC_FRONTIER}; - table[OP_SWAP4] = {"SWAP4", 0, false, 5, 0, EVMC_FRONTIER}; - table[OP_SWAP5] = {"SWAP5", 0, false, 6, 0, EVMC_FRONTIER}; - table[OP_SWAP6] = {"SWAP6", 0, false, 7, 0, EVMC_FRONTIER}; - table[OP_SWAP7] = {"SWAP7", 0, false, 8, 0, EVMC_FRONTIER}; - table[OP_SWAP8] = {"SWAP8", 0, false, 9, 0, EVMC_FRONTIER}; - table[OP_SWAP9] = {"SWAP9", 0, false, 10, 0, EVMC_FRONTIER}; - table[OP_SWAP10] = {"SWAP10", 0, false, 11, 0, EVMC_FRONTIER}; - table[OP_SWAP11] = {"SWAP11", 0, false, 12, 0, EVMC_FRONTIER}; - table[OP_SWAP12] = {"SWAP12", 0, false, 13, 0, EVMC_FRONTIER}; - table[OP_SWAP13] = {"SWAP13", 0, false, 14, 0, EVMC_FRONTIER}; - table[OP_SWAP14] = {"SWAP14", 0, false, 15, 0, EVMC_FRONTIER}; - table[OP_SWAP15] = {"SWAP15", 0, false, 16, 0, EVMC_FRONTIER}; - table[OP_SWAP16] = {"SWAP16", 0, false, 17, 0, EVMC_FRONTIER}; - - table[OP_LOG0] = {"LOG0", 0, false, 2, -2, EVMC_FRONTIER}; - table[OP_LOG1] = {"LOG1", 0, false, 3, -3, EVMC_FRONTIER}; - table[OP_LOG2] = {"LOG2", 0, false, 4, -4, EVMC_FRONTIER}; - table[OP_LOG3] = {"LOG3", 0, false, 5, -5, EVMC_FRONTIER}; - table[OP_LOG4] = {"LOG4", 0, false, 6, -6, EVMC_FRONTIER}; - - table[OP_DUPN] = {"DUPN", 1, false, 0, 1, EVMC_CANCUN}; - table[OP_SWAPN] = {"SWAPN", 1, false, 0, 0, EVMC_CANCUN}; + "RJUMPV", 1 /* 1 byte static immediate + dynamic immediate */, false, 1, -1, {}, REV_EOF1}; + + table[OP_TLOAD] = {"TLOAD", 0, false, 1, 0, EVMC_CANCUN, REV_EOF1}; + table[OP_TSTORE] = {"TSTORE", 0, false, 2, -2, EVMC_CANCUN, REV_EOF1}; + table[OP_PUSH0] = {"PUSH0", 0, false, 0, 1, EVMC_SHANGHAI, REV_EOF1}; + + table[OP_PUSH1] = {"PUSH1", 1, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH2] = {"PUSH2", 2, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH3] = {"PUSH3", 3, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH4] = {"PUSH4", 4, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH5] = {"PUSH5", 5, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH6] = {"PUSH6", 6, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH7] = {"PUSH7", 7, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH8] = {"PUSH8", 8, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH9] = {"PUSH9", 9, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH10] = {"PUSH10", 10, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH11] = {"PUSH11", 11, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH12] = {"PUSH12", 12, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH13] = {"PUSH13", 13, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH14] = {"PUSH14", 14, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH15] = {"PUSH15", 15, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH16] = {"PUSH16", 16, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH17] = {"PUSH17", 17, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH18] = {"PUSH18", 18, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH19] = {"PUSH19", 19, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH20] = {"PUSH20", 20, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH21] = {"PUSH21", 21, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH22] = {"PUSH22", 22, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH23] = {"PUSH23", 23, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH24] = {"PUSH24", 24, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH25] = {"PUSH25", 25, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH26] = {"PUSH26", 26, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH27] = {"PUSH27", 27, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH28] = {"PUSH28", 28, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH29] = {"PUSH29", 29, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH30] = {"PUSH30", 30, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH31] = {"PUSH31", 31, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_PUSH32] = {"PUSH32", 32, false, 0, 1, EVMC_FRONTIER, REV_EOF1}; + + table[OP_DUP1] = {"DUP1", 0, false, 1, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP2] = {"DUP2", 0, false, 2, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP3] = {"DUP3", 0, false, 3, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP4] = {"DUP4", 0, false, 4, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP5] = {"DUP5", 0, false, 5, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP6] = {"DUP6", 0, false, 6, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP7] = {"DUP7", 0, false, 7, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP8] = {"DUP8", 0, false, 8, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP9] = {"DUP9", 0, false, 9, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP10] = {"DUP10", 0, false, 10, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP11] = {"DUP11", 0, false, 11, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP12] = {"DUP12", 0, false, 12, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP13] = {"DUP13", 0, false, 13, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP14] = {"DUP14", 0, false, 14, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP15] = {"DUP15", 0, false, 15, 1, EVMC_FRONTIER, REV_EOF1}; + table[OP_DUP16] = {"DUP16", 0, false, 16, 1, EVMC_FRONTIER, REV_EOF1}; + + table[OP_SWAP1] = {"SWAP1", 0, false, 2, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP2] = {"SWAP2", 0, false, 3, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP3] = {"SWAP3", 0, false, 4, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP4] = {"SWAP4", 0, false, 5, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP5] = {"SWAP5", 0, false, 6, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP6] = {"SWAP6", 0, false, 7, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP7] = {"SWAP7", 0, false, 8, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP8] = {"SWAP8", 0, false, 9, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP9] = {"SWAP9", 0, false, 10, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP10] = {"SWAP10", 0, false, 11, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP11] = {"SWAP11", 0, false, 12, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP12] = {"SWAP12", 0, false, 13, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP13] = {"SWAP13", 0, false, 14, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP14] = {"SWAP14", 0, false, 15, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP15] = {"SWAP15", 0, false, 16, 0, EVMC_FRONTIER, REV_EOF1}; + table[OP_SWAP16] = {"SWAP16", 0, false, 17, 0, EVMC_FRONTIER, REV_EOF1}; + + table[OP_LOG0] = {"LOG0", 0, false, 2, -2, EVMC_FRONTIER, REV_EOF1}; + table[OP_LOG1] = {"LOG1", 0, false, 3, -3, EVMC_FRONTIER, REV_EOF1}; + table[OP_LOG2] = {"LOG2", 0, false, 4, -4, EVMC_FRONTIER, REV_EOF1}; + table[OP_LOG3] = {"LOG3", 0, false, 5, -5, EVMC_FRONTIER, REV_EOF1}; + table[OP_LOG4] = {"LOG4", 0, false, 6, -6, EVMC_FRONTIER, REV_EOF1}; + + table[OP_DUPN] = {"DUPN", 1, false, 0, 1, {}, REV_EOF1}; + table[OP_SWAPN] = {"SWAPN", 1, false, 0, 0, {}, REV_EOF1}; + table[OP_EXCHANGE] = {"EXCHANGE", 1, false, 0, 0, {}, REV_EOF1}; + table[OP_MCOPY] = {"MCOPY", 0, false, 3, -3, EVMC_CANCUN, REV_EOF1}; + table[OP_DATALOAD] = {"DATALOAD", 0, false, 1, 0, {}, REV_EOF1}; + table[OP_DATALOADN] = {"DATALOADN", 2, false, 0, 1, {}, REV_EOF1}; + table[OP_DATASIZE] = {"DATASIZE", 0, false, 0, 1, {}, REV_EOF1}; + table[OP_DATACOPY] = {"DATACOPY", 0, false, 3, -3, {}, REV_EOF1}; table[OP_CREATE] = {"CREATE", 0, false, 3, -2, EVMC_FRONTIER}; table[OP_CALL] = {"CALL", 0, false, 7, -6, EVMC_FRONTIER}; table[OP_CALLCODE] = {"CALLCODE", 0, false, 7, -6, EVMC_FRONTIER}; - table[OP_RETURN] = {"RETURN", 0, true, 2, -2, EVMC_FRONTIER}; + table[OP_RETURN] = {"RETURN", 0, true, 2, -2, EVMC_FRONTIER, REV_EOF1}; table[OP_DELEGATECALL] = {"DELEGATECALL", 0, false, 6, -5, EVMC_HOMESTEAD}; table[OP_CREATE2] = {"CREATE2", 0, false, 4, -3, EVMC_CONSTANTINOPLE}; + table[OP_RETURNDATALOAD] = {"RETURNDATALOAD", 0, false, 1, 0, {}, REV_EOF1}; + table[OP_EOFCREATE] = {"EOFCREATE", 1, false, 4, -3, {}, REV_EOF1}; + table[OP_RETURNCONTRACT] = {"RETURNCONTRACT", 1, true, 2, -2, {}, REV_EOF1}; + table[OP_EXTCALL] = {"EXTCALL", 0, false, 4, -3, {}, REV_EOF1}; + table[OP_EXTDELEGATECALL] = {"EXTDELEGATECALL", 0, false, 3, -2, {}, REV_EOF1}; table[OP_STATICCALL] = {"STATICCALL", 0, false, 6, -5, EVMC_BYZANTIUM}; - table[OP_CALLF] = {"CALLF", 2, false, 0, 0, EVMC_CANCUN}; - table[OP_RETF] = {"RETF", 0, true, 0, 0, EVMC_CANCUN}; - table[OP_REVERT] = {"REVERT", 0, true, 2, -2, EVMC_BYZANTIUM}; - table[OP_INVALID] = {"INVALID", 0, true, 0, 0, EVMC_FRONTIER}; + table[OP_EXTSTATICCALL] = {"EXTSTATICCALL", 0, false, 3, -2, {}, REV_EOF1}; + table[OP_CALLF] = {"CALLF", 2, false, 0, 0, {}, REV_EOF1}; + table[OP_RETF] = {"RETF", 0, true, 0, 0, {}, REV_EOF1}; + table[OP_JUMPF] = {"JUMPF", 2, true, 0, 0, {}, REV_EOF1}; + table[OP_REVERT] = {"REVERT", 0, true, 2, -2, EVMC_BYZANTIUM, REV_EOF1}; + table[OP_INVALID] = {"INVALID", 0, true, 0, 0, EVMC_FRONTIER, REV_EOF1}; table[OP_SELFDESTRUCT] = {"SELFDESTRUCT", 0, true, 1, -1, EVMC_FRONTIER}; return table; diff --git a/lib/evmone/instructions_xmacro.hpp b/lib/evmone/instructions_xmacro.hpp index 7c81d7f1..2897952d 100644 --- a/lib/evmone/instructions_xmacro.hpp +++ b/lib/evmone/instructions_xmacro.hpp @@ -31,275 +31,275 @@ /// undef it and restore the alias after usage. /// /// See for more about X Macros: https://en.wikipedia.org/wiki/X_Macro. -#define MAP_OPCODES \ - ON_OPCODE_IDENTIFIER(OP_STOP, stop) \ - ON_OPCODE_IDENTIFIER(OP_ADD, add) \ - ON_OPCODE_IDENTIFIER(OP_MUL, mul) \ - ON_OPCODE_IDENTIFIER(OP_SUB, sub) \ - ON_OPCODE_IDENTIFIER(OP_DIV, div) \ - ON_OPCODE_IDENTIFIER(OP_SDIV, sdiv) \ - ON_OPCODE_IDENTIFIER(OP_MOD, mod) \ - ON_OPCODE_IDENTIFIER(OP_SMOD, smod) \ - ON_OPCODE_IDENTIFIER(OP_ADDMOD, addmod) \ - ON_OPCODE_IDENTIFIER(OP_MULMOD, mulmod) \ - ON_OPCODE_IDENTIFIER(OP_EXP, exp) \ - ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, signextend) \ - ON_OPCODE_UNDEFINED(0x0c) \ - ON_OPCODE_UNDEFINED(0x0d) \ - ON_OPCODE_UNDEFINED(0x0e) \ - ON_OPCODE_UNDEFINED(0x0f) \ - \ - ON_OPCODE_IDENTIFIER(OP_LT, lt) \ - ON_OPCODE_IDENTIFIER(OP_GT, gt) \ - ON_OPCODE_IDENTIFIER(OP_SLT, slt) \ - ON_OPCODE_IDENTIFIER(OP_SGT, sgt) \ - ON_OPCODE_IDENTIFIER(OP_EQ, eq) \ - ON_OPCODE_IDENTIFIER(OP_ISZERO, iszero) \ - ON_OPCODE_IDENTIFIER(OP_AND, and_) \ - ON_OPCODE_IDENTIFIER(OP_OR, or_) \ - ON_OPCODE_IDENTIFIER(OP_XOR, xor_) \ - ON_OPCODE_IDENTIFIER(OP_NOT, not_) \ - ON_OPCODE_IDENTIFIER(OP_BYTE, byte) \ - ON_OPCODE_IDENTIFIER(OP_SHL, shl) \ - ON_OPCODE_IDENTIFIER(OP_SHR, shr) \ - ON_OPCODE_IDENTIFIER(OP_SAR, sar) \ - ON_OPCODE_UNDEFINED(0x1e) \ - ON_OPCODE_UNDEFINED(0x1f) \ - \ - ON_OPCODE_IDENTIFIER(OP_KECCAK256, keccak256) \ - ON_OPCODE_UNDEFINED(0x21) \ - ON_OPCODE_UNDEFINED(0x22) \ - ON_OPCODE_UNDEFINED(0x23) \ - ON_OPCODE_UNDEFINED(0x24) \ - ON_OPCODE_UNDEFINED(0x25) \ - ON_OPCODE_UNDEFINED(0x26) \ - ON_OPCODE_UNDEFINED(0x27) \ - ON_OPCODE_UNDEFINED(0x28) \ - ON_OPCODE_UNDEFINED(0x29) \ - ON_OPCODE_UNDEFINED(0x2a) \ - ON_OPCODE_UNDEFINED(0x2b) \ - ON_OPCODE_UNDEFINED(0x2c) \ - ON_OPCODE_UNDEFINED(0x2d) \ - ON_OPCODE_UNDEFINED(0x2e) \ - ON_OPCODE_UNDEFINED(0x2f) \ - \ - ON_OPCODE_IDENTIFIER(OP_ADDRESS, address) \ - ON_OPCODE_IDENTIFIER(OP_BALANCE, balance) \ - ON_OPCODE_IDENTIFIER(OP_ORIGIN, origin) \ - ON_OPCODE_IDENTIFIER(OP_CALLER, caller) \ - ON_OPCODE_IDENTIFIER(OP_CALLVALUE, callvalue) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, calldataload) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, calldatasize) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, calldatacopy) \ - ON_OPCODE_IDENTIFIER(OP_CODESIZE, codesize) \ - ON_OPCODE_IDENTIFIER(OP_CODECOPY, codecopy) \ - ON_OPCODE_IDENTIFIER(OP_GASPRICE, gasprice) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, extcodesize) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, extcodecopy) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, returndatasize) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, returndatacopy) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, extcodehash) \ - \ - ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, blockhash) \ - ON_OPCODE_IDENTIFIER(OP_COINBASE, coinbase) \ - ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, timestamp) \ - ON_OPCODE_IDENTIFIER(OP_NUMBER, number) \ - ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, prevrandao) \ - ON_OPCODE_IDENTIFIER(OP_GASLIMIT, gaslimit) \ - ON_OPCODE_IDENTIFIER(OP_CHAINID, chainid) \ - ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, selfbalance) \ - ON_OPCODE_IDENTIFIER(OP_BASEFEE, basefee) \ - ON_OPCODE_UNDEFINED(0x49) \ - ON_OPCODE_UNDEFINED(0x4a) \ - ON_OPCODE_UNDEFINED(0x4b) \ - ON_OPCODE_UNDEFINED(0x4c) \ - ON_OPCODE_UNDEFINED(0x4d) \ - ON_OPCODE_UNDEFINED(0x4e) \ - ON_OPCODE_UNDEFINED(0x4f) \ - \ - ON_OPCODE_IDENTIFIER(OP_POP, pop) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD, mload) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE, mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE8, mstore8) \ - ON_OPCODE_IDENTIFIER(OP_SLOAD, sload) \ - ON_OPCODE_IDENTIFIER(OP_SSTORE, sstore) \ - ON_OPCODE_IDENTIFIER(OP_JUMP, jump) \ - ON_OPCODE_IDENTIFIER(OP_JUMPI, jumpi) \ - ON_OPCODE_IDENTIFIER(OP_PC, pc) \ - ON_OPCODE_IDENTIFIER(OP_MSIZE, msize) \ - ON_OPCODE_IDENTIFIER(OP_GAS, gas) \ - ON_OPCODE_IDENTIFIER(OP_JUMPDEST, jumpdest) \ - ON_OPCODE_IDENTIFIER(OP_RJUMP, rjump) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPI, rjumpi) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPV, rjumpv) \ - ON_OPCODE_IDENTIFIER(OP_PUSH0, push0) \ - \ - ON_OPCODE_IDENTIFIER(OP_PUSH1, push<1>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH2, push<2>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH3, push<3>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH4, push<4>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH5, push<5>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH6, push<6>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH7, push<7>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH8, push<8>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH9, push<9>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH10, push<10>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH11, push<11>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH12, push<12>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH13, push<13>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH14, push<14>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH15, push<15>) \ - \ - ON_OPCODE_IDENTIFIER(OP_PUSH16, push<16>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH17, push<17>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH18, push<18>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH19, push<19>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH20, push<20>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH21, push<21>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH22, push<22>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH23, push<23>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH24, push<24>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH25, push<25>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH26, push<26>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH27, push<27>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH28, push<28>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH29, push<29>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH30, push<30>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH31, push<31>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH32, push<32>) \ - \ - ON_OPCODE_IDENTIFIER(OP_DUP1, dup<1>) \ - ON_OPCODE_IDENTIFIER(OP_DUP2, dup<2>) \ - ON_OPCODE_IDENTIFIER(OP_DUP3, dup<3>) \ - ON_OPCODE_IDENTIFIER(OP_DUP4, dup<4>) \ - ON_OPCODE_IDENTIFIER(OP_DUP5, dup<5>) \ - ON_OPCODE_IDENTIFIER(OP_DUP6, dup<6>) \ - ON_OPCODE_IDENTIFIER(OP_DUP7, dup<7>) \ - ON_OPCODE_IDENTIFIER(OP_DUP8, dup<8>) \ - ON_OPCODE_IDENTIFIER(OP_DUP9, dup<9>) \ - ON_OPCODE_IDENTIFIER(OP_DUP10, dup<10>) \ - ON_OPCODE_IDENTIFIER(OP_DUP11, dup<11>) \ - ON_OPCODE_IDENTIFIER(OP_DUP12, dup<12>) \ - ON_OPCODE_IDENTIFIER(OP_DUP13, dup<13>) \ - ON_OPCODE_IDENTIFIER(OP_DUP14, dup<14>) \ - ON_OPCODE_IDENTIFIER(OP_DUP15, dup<15>) \ - ON_OPCODE_IDENTIFIER(OP_DUP16, dup<16>) \ - \ - ON_OPCODE_IDENTIFIER(OP_SWAP1, swap<1>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP2, swap<2>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP3, swap<3>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP4, swap<4>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP5, swap<5>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP6, swap<6>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP7, swap<7>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP8, swap<8>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP9, swap<9>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP10, swap<10>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP11, swap<11>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP12, swap<12>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP13, swap<13>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP14, swap<14>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP15, swap<15>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP16, swap<16>) \ - \ - ON_OPCODE_IDENTIFIER(OP_LOG0, log<0>) \ - ON_OPCODE_IDENTIFIER(OP_LOG1, log<1>) \ - ON_OPCODE_IDENTIFIER(OP_LOG2, log<2>) \ - ON_OPCODE_IDENTIFIER(OP_LOG3, log<3>) \ - ON_OPCODE_IDENTIFIER(OP_LOG4, log<4>) \ - ON_OPCODE_UNDEFINED(0xa5) \ - ON_OPCODE_UNDEFINED(0xa6) \ - ON_OPCODE_UNDEFINED(0xa7) \ - ON_OPCODE_UNDEFINED(0xa8) \ - ON_OPCODE_UNDEFINED(0xa9) \ - ON_OPCODE_UNDEFINED(0xaa) \ - ON_OPCODE_UNDEFINED(0xab) \ - ON_OPCODE_UNDEFINED(0xac) \ - ON_OPCODE_UNDEFINED(0xad) \ - ON_OPCODE_UNDEFINED(0xae) \ - ON_OPCODE_UNDEFINED(0xaf) \ - \ - ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \ - ON_OPCODE_IDENTIFIER(OP_RETF, retf) \ - ON_OPCODE_UNDEFINED(0xb2) \ - ON_OPCODE_UNDEFINED(0xb3) \ - ON_OPCODE_UNDEFINED(0xb4) \ - ON_OPCODE_IDENTIFIER(OP_DUPN, dupn) \ - ON_OPCODE_IDENTIFIER(OP_SWAPN, swapn) \ - ON_OPCODE_UNDEFINED(0xb7) \ - ON_OPCODE_UNDEFINED(0xb8) \ - ON_OPCODE_UNDEFINED(0xb9) \ - ON_OPCODE_UNDEFINED(0xba) \ - ON_OPCODE_UNDEFINED(0xbb) \ - ON_OPCODE_UNDEFINED(0xbc) \ - ON_OPCODE_UNDEFINED(0xbd) \ - ON_OPCODE_UNDEFINED(0xbe) \ - ON_OPCODE_UNDEFINED(0xbf) \ - \ - ON_OPCODE_UNDEFINED(0xc0) \ - ON_OPCODE_UNDEFINED(0xc1) \ - ON_OPCODE_UNDEFINED(0xc2) \ - ON_OPCODE_UNDEFINED(0xc3) \ - ON_OPCODE_UNDEFINED(0xc4) \ - ON_OPCODE_UNDEFINED(0xc5) \ - ON_OPCODE_UNDEFINED(0xc6) \ - ON_OPCODE_UNDEFINED(0xc7) \ - ON_OPCODE_UNDEFINED(0xc8) \ - ON_OPCODE_UNDEFINED(0xc9) \ - ON_OPCODE_UNDEFINED(0xca) \ - ON_OPCODE_UNDEFINED(0xcb) \ - ON_OPCODE_UNDEFINED(0xcc) \ - ON_OPCODE_UNDEFINED(0xcd) \ - ON_OPCODE_UNDEFINED(0xce) \ - ON_OPCODE_UNDEFINED(0xcf) \ - \ - ON_OPCODE_UNDEFINED(0xd0) \ - ON_OPCODE_UNDEFINED(0xd1) \ - ON_OPCODE_UNDEFINED(0xd2) \ - ON_OPCODE_UNDEFINED(0xd3) \ - ON_OPCODE_UNDEFINED(0xd4) \ - ON_OPCODE_UNDEFINED(0xd5) \ - ON_OPCODE_UNDEFINED(0xd6) \ - ON_OPCODE_UNDEFINED(0xd7) \ - ON_OPCODE_UNDEFINED(0xd8) \ - ON_OPCODE_UNDEFINED(0xd9) \ - ON_OPCODE_UNDEFINED(0xda) \ - ON_OPCODE_UNDEFINED(0xdb) \ - ON_OPCODE_UNDEFINED(0xdc) \ - ON_OPCODE_UNDEFINED(0xdd) \ - ON_OPCODE_UNDEFINED(0xde) \ - ON_OPCODE_UNDEFINED(0xdf) \ - \ - ON_OPCODE_UNDEFINED(0xe0) \ - ON_OPCODE_UNDEFINED(0xe1) \ - ON_OPCODE_UNDEFINED(0xe2) \ - ON_OPCODE_UNDEFINED(0xe3) \ - ON_OPCODE_UNDEFINED(0xe4) \ - ON_OPCODE_UNDEFINED(0xe5) \ - ON_OPCODE_UNDEFINED(0xe6) \ - ON_OPCODE_UNDEFINED(0xe7) \ - ON_OPCODE_UNDEFINED(0xe8) \ - ON_OPCODE_UNDEFINED(0xe9) \ - ON_OPCODE_UNDEFINED(0xea) \ - ON_OPCODE_UNDEFINED(0xeb) \ - ON_OPCODE_UNDEFINED(0xec) \ - ON_OPCODE_UNDEFINED(0xed) \ - ON_OPCODE_UNDEFINED(0xee) \ - ON_OPCODE_UNDEFINED(0xef) \ - \ - ON_OPCODE_IDENTIFIER(OP_CREATE, create) \ - ON_OPCODE_IDENTIFIER(OP_CALL, call) \ - ON_OPCODE_IDENTIFIER(OP_CALLCODE, callcode) \ - ON_OPCODE_IDENTIFIER(OP_RETURN, return_) \ - ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, delegatecall) \ - ON_OPCODE_IDENTIFIER(OP_CREATE2, create2) \ - ON_OPCODE_UNDEFINED(0xf6) \ - ON_OPCODE_UNDEFINED(0xf7) \ - ON_OPCODE_UNDEFINED(0xf8) \ - ON_OPCODE_UNDEFINED(0xf9) \ - ON_OPCODE_IDENTIFIER(OP_STATICCALL, staticcall) \ - ON_OPCODE_UNDEFINED(0xfb) \ - ON_OPCODE_UNDEFINED(0xfc) \ - ON_OPCODE_IDENTIFIER(OP_REVERT, revert) \ - ON_OPCODE_IDENTIFIER(OP_INVALID, invalid) \ +#define MAP_OPCODES \ + ON_OPCODE_IDENTIFIER(OP_STOP, stop) \ + ON_OPCODE_IDENTIFIER(OP_ADD, add) \ + ON_OPCODE_IDENTIFIER(OP_MUL, mul) \ + ON_OPCODE_IDENTIFIER(OP_SUB, sub) \ + ON_OPCODE_IDENTIFIER(OP_DIV, div) \ + ON_OPCODE_IDENTIFIER(OP_SDIV, sdiv) \ + ON_OPCODE_IDENTIFIER(OP_MOD, mod) \ + ON_OPCODE_IDENTIFIER(OP_SMOD, smod) \ + ON_OPCODE_IDENTIFIER(OP_ADDMOD, addmod) \ + ON_OPCODE_IDENTIFIER(OP_MULMOD, mulmod) \ + ON_OPCODE_IDENTIFIER(OP_EXP, exp) \ + ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, signextend) \ + ON_OPCODE_UNDEFINED(0x0c) \ + ON_OPCODE_UNDEFINED(0x0d) \ + ON_OPCODE_UNDEFINED(0x0e) \ + ON_OPCODE_UNDEFINED(0x0f) \ + \ + ON_OPCODE_IDENTIFIER(OP_LT, lt) \ + ON_OPCODE_IDENTIFIER(OP_GT, gt) \ + ON_OPCODE_IDENTIFIER(OP_SLT, slt) \ + ON_OPCODE_IDENTIFIER(OP_SGT, sgt) \ + ON_OPCODE_IDENTIFIER(OP_EQ, eq) \ + ON_OPCODE_IDENTIFIER(OP_ISZERO, iszero) \ + ON_OPCODE_IDENTIFIER(OP_AND, and_) \ + ON_OPCODE_IDENTIFIER(OP_OR, or_) \ + ON_OPCODE_IDENTIFIER(OP_XOR, xor_) \ + ON_OPCODE_IDENTIFIER(OP_NOT, not_) \ + ON_OPCODE_IDENTIFIER(OP_BYTE, byte) \ + ON_OPCODE_IDENTIFIER(OP_SHL, shl) \ + ON_OPCODE_IDENTIFIER(OP_SHR, shr) \ + ON_OPCODE_IDENTIFIER(OP_SAR, sar) \ + ON_OPCODE_UNDEFINED(0x1e) \ + ON_OPCODE_UNDEFINED(0x1f) \ + \ + ON_OPCODE_IDENTIFIER(OP_KECCAK256, keccak256) \ + ON_OPCODE_UNDEFINED(0x21) \ + ON_OPCODE_UNDEFINED(0x22) \ + ON_OPCODE_UNDEFINED(0x23) \ + ON_OPCODE_UNDEFINED(0x24) \ + ON_OPCODE_UNDEFINED(0x25) \ + ON_OPCODE_UNDEFINED(0x26) \ + ON_OPCODE_UNDEFINED(0x27) \ + ON_OPCODE_UNDEFINED(0x28) \ + ON_OPCODE_UNDEFINED(0x29) \ + ON_OPCODE_UNDEFINED(0x2a) \ + ON_OPCODE_UNDEFINED(0x2b) \ + ON_OPCODE_UNDEFINED(0x2c) \ + ON_OPCODE_UNDEFINED(0x2d) \ + ON_OPCODE_UNDEFINED(0x2e) \ + ON_OPCODE_UNDEFINED(0x2f) \ + \ + ON_OPCODE_IDENTIFIER(OP_ADDRESS, address) \ + ON_OPCODE_IDENTIFIER(OP_BALANCE, balance) \ + ON_OPCODE_IDENTIFIER(OP_ORIGIN, origin) \ + ON_OPCODE_IDENTIFIER(OP_CALLER, caller) \ + ON_OPCODE_IDENTIFIER(OP_CALLVALUE, callvalue) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, calldataload) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, calldatasize) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, calldatacopy) \ + ON_OPCODE_IDENTIFIER(OP_CODESIZE, codesize) \ + ON_OPCODE_IDENTIFIER(OP_CODECOPY, codecopy) \ + ON_OPCODE_IDENTIFIER(OP_GASPRICE, gasprice) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, extcodesize) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, extcodecopy) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, returndatasize) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, returndatacopy) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, extcodehash) \ + \ + ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, blockhash) \ + ON_OPCODE_IDENTIFIER(OP_COINBASE, coinbase) \ + ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, timestamp) \ + ON_OPCODE_IDENTIFIER(OP_NUMBER, number) \ + ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, prevrandao) \ + ON_OPCODE_IDENTIFIER(OP_GASLIMIT, gaslimit) \ + ON_OPCODE_IDENTIFIER(OP_CHAINID, chainid) \ + ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, selfbalance) \ + ON_OPCODE_IDENTIFIER(OP_BASEFEE, basefee) \ + ON_OPCODE_IDENTIFIER(OP_BLOBHASH, blobhash) \ + ON_OPCODE_IDENTIFIER(OP_BLOBBASEFEE, blobbasefee) \ + ON_OPCODE_UNDEFINED(0x4b) \ + ON_OPCODE_UNDEFINED(0x4c) \ + ON_OPCODE_UNDEFINED(0x4d) \ + ON_OPCODE_UNDEFINED(0x4e) \ + ON_OPCODE_UNDEFINED(0x4f) \ + \ + ON_OPCODE_IDENTIFIER(OP_POP, pop) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD, mload) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE, mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE8, mstore8) \ + ON_OPCODE_IDENTIFIER(OP_SLOAD, sload) \ + ON_OPCODE_IDENTIFIER(OP_SSTORE, sstore) \ + ON_OPCODE_IDENTIFIER(OP_JUMP, jump) \ + ON_OPCODE_IDENTIFIER(OP_JUMPI, jumpi) \ + ON_OPCODE_IDENTIFIER(OP_PC, pc) \ + ON_OPCODE_IDENTIFIER(OP_MSIZE, msize) \ + ON_OPCODE_IDENTIFIER(OP_GAS, gas) \ + ON_OPCODE_IDENTIFIER(OP_JUMPDEST, jumpdest) \ + ON_OPCODE_IDENTIFIER(OP_TLOAD, tload) \ + ON_OPCODE_IDENTIFIER(OP_TSTORE, tstore) \ + ON_OPCODE_IDENTIFIER(OP_MCOPY, mcopy) \ + ON_OPCODE_IDENTIFIER(OP_PUSH0, push0) \ + \ + ON_OPCODE_IDENTIFIER(OP_PUSH1, push<1>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH2, push<2>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH3, push<3>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH4, push<4>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH5, push<5>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH6, push<6>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH7, push<7>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH8, push<8>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH9, push<9>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH10, push<10>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH11, push<11>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH12, push<12>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH13, push<13>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH14, push<14>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH15, push<15>) \ + \ + ON_OPCODE_IDENTIFIER(OP_PUSH16, push<16>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH17, push<17>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH18, push<18>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH19, push<19>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH20, push<20>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH21, push<21>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH22, push<22>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH23, push<23>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH24, push<24>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH25, push<25>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH26, push<26>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH27, push<27>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH28, push<28>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH29, push<29>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH30, push<30>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH31, push<31>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH32, push<32>) \ + \ + ON_OPCODE_IDENTIFIER(OP_DUP1, dup<1>) \ + ON_OPCODE_IDENTIFIER(OP_DUP2, dup<2>) \ + ON_OPCODE_IDENTIFIER(OP_DUP3, dup<3>) \ + ON_OPCODE_IDENTIFIER(OP_DUP4, dup<4>) \ + ON_OPCODE_IDENTIFIER(OP_DUP5, dup<5>) \ + ON_OPCODE_IDENTIFIER(OP_DUP6, dup<6>) \ + ON_OPCODE_IDENTIFIER(OP_DUP7, dup<7>) \ + ON_OPCODE_IDENTIFIER(OP_DUP8, dup<8>) \ + ON_OPCODE_IDENTIFIER(OP_DUP9, dup<9>) \ + ON_OPCODE_IDENTIFIER(OP_DUP10, dup<10>) \ + ON_OPCODE_IDENTIFIER(OP_DUP11, dup<11>) \ + ON_OPCODE_IDENTIFIER(OP_DUP12, dup<12>) \ + ON_OPCODE_IDENTIFIER(OP_DUP13, dup<13>) \ + ON_OPCODE_IDENTIFIER(OP_DUP14, dup<14>) \ + ON_OPCODE_IDENTIFIER(OP_DUP15, dup<15>) \ + ON_OPCODE_IDENTIFIER(OP_DUP16, dup<16>) \ + \ + ON_OPCODE_IDENTIFIER(OP_SWAP1, swap<1>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP2, swap<2>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP3, swap<3>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP4, swap<4>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP5, swap<5>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP6, swap<6>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP7, swap<7>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP8, swap<8>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP9, swap<9>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP10, swap<10>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP11, swap<11>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP12, swap<12>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP13, swap<13>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP14, swap<14>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP15, swap<15>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP16, swap<16>) \ + \ + ON_OPCODE_IDENTIFIER(OP_LOG0, log<0>) \ + ON_OPCODE_IDENTIFIER(OP_LOG1, log<1>) \ + ON_OPCODE_IDENTIFIER(OP_LOG2, log<2>) \ + ON_OPCODE_IDENTIFIER(OP_LOG3, log<3>) \ + ON_OPCODE_IDENTIFIER(OP_LOG4, log<4>) \ + ON_OPCODE_UNDEFINED(0xa5) \ + ON_OPCODE_UNDEFINED(0xa6) \ + ON_OPCODE_UNDEFINED(0xa7) \ + ON_OPCODE_UNDEFINED(0xa8) \ + ON_OPCODE_UNDEFINED(0xa9) \ + ON_OPCODE_UNDEFINED(0xaa) \ + ON_OPCODE_UNDEFINED(0xab) \ + ON_OPCODE_UNDEFINED(0xac) \ + ON_OPCODE_UNDEFINED(0xad) \ + ON_OPCODE_UNDEFINED(0xae) \ + ON_OPCODE_UNDEFINED(0xaf) \ + \ + ON_OPCODE_UNDEFINED(0xb0) \ + ON_OPCODE_UNDEFINED(0xb1) \ + ON_OPCODE_UNDEFINED(0xb2) \ + ON_OPCODE_UNDEFINED(0xb3) \ + ON_OPCODE_UNDEFINED(0xb4) \ + ON_OPCODE_UNDEFINED(0xb5) \ + ON_OPCODE_UNDEFINED(0xb6) \ + ON_OPCODE_UNDEFINED(0xb7) \ + ON_OPCODE_UNDEFINED(0xb8) \ + ON_OPCODE_UNDEFINED(0xb9) \ + ON_OPCODE_UNDEFINED(0xba) \ + ON_OPCODE_UNDEFINED(0xbb) \ + ON_OPCODE_UNDEFINED(0xbc) \ + ON_OPCODE_UNDEFINED(0xbd) \ + ON_OPCODE_UNDEFINED(0xbe) \ + ON_OPCODE_UNDEFINED(0xbf) \ + \ + ON_OPCODE_UNDEFINED(0xc0) \ + ON_OPCODE_UNDEFINED(0xc1) \ + ON_OPCODE_UNDEFINED(0xc2) \ + ON_OPCODE_UNDEFINED(0xc3) \ + ON_OPCODE_UNDEFINED(0xc4) \ + ON_OPCODE_UNDEFINED(0xc5) \ + ON_OPCODE_UNDEFINED(0xc6) \ + ON_OPCODE_UNDEFINED(0xc7) \ + ON_OPCODE_UNDEFINED(0xc8) \ + ON_OPCODE_UNDEFINED(0xc9) \ + ON_OPCODE_UNDEFINED(0xca) \ + ON_OPCODE_UNDEFINED(0xcb) \ + ON_OPCODE_UNDEFINED(0xcc) \ + ON_OPCODE_UNDEFINED(0xcd) \ + ON_OPCODE_UNDEFINED(0xce) \ + ON_OPCODE_UNDEFINED(0xcf) \ + \ + ON_OPCODE_IDENTIFIER(OP_DATALOAD, dataload) \ + ON_OPCODE_IDENTIFIER(OP_DATALOADN, dataloadn) \ + ON_OPCODE_IDENTIFIER(OP_DATASIZE, datasize) \ + ON_OPCODE_IDENTIFIER(OP_DATACOPY, datacopy) \ + ON_OPCODE_UNDEFINED(0xd4) \ + ON_OPCODE_UNDEFINED(0xd5) \ + ON_OPCODE_UNDEFINED(0xd6) \ + ON_OPCODE_UNDEFINED(0xd7) \ + ON_OPCODE_UNDEFINED(0xd8) \ + ON_OPCODE_UNDEFINED(0xd9) \ + ON_OPCODE_UNDEFINED(0xda) \ + ON_OPCODE_UNDEFINED(0xdb) \ + ON_OPCODE_UNDEFINED(0xdc) \ + ON_OPCODE_UNDEFINED(0xdd) \ + ON_OPCODE_UNDEFINED(0xde) \ + ON_OPCODE_UNDEFINED(0xdf) \ + \ + ON_OPCODE_IDENTIFIER(OP_RJUMP, rjump) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPI, rjumpi) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPV, rjumpv) \ + ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \ + ON_OPCODE_IDENTIFIER(OP_RETF, retf) \ + ON_OPCODE_IDENTIFIER(OP_JUMPF, jumpf) \ + ON_OPCODE_IDENTIFIER(OP_DUPN, dupn) \ + ON_OPCODE_IDENTIFIER(OP_SWAPN, swapn) \ + ON_OPCODE_IDENTIFIER(OP_EXCHANGE, exchange) \ + ON_OPCODE_UNDEFINED(0xe9) \ + ON_OPCODE_UNDEFINED(0xea) \ + ON_OPCODE_UNDEFINED(0xeb) \ + ON_OPCODE_IDENTIFIER(OP_EOFCREATE, eofcreate) \ + ON_OPCODE_UNDEFINED(0xed) \ + ON_OPCODE_IDENTIFIER(OP_RETURNCONTRACT, returncontract) \ + ON_OPCODE_UNDEFINED(0xef) \ + \ + ON_OPCODE_IDENTIFIER(OP_CREATE, create) \ + ON_OPCODE_IDENTIFIER(OP_CALL, call) \ + ON_OPCODE_IDENTIFIER(OP_CALLCODE, callcode) \ + ON_OPCODE_IDENTIFIER(OP_RETURN, return_) \ + ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, delegatecall) \ + ON_OPCODE_IDENTIFIER(OP_CREATE2, create2) \ + ON_OPCODE_UNDEFINED(0xf6) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATALOAD, returndataload) \ + ON_OPCODE_IDENTIFIER(OP_EXTCALL, extcall) \ + ON_OPCODE_IDENTIFIER(OP_EXTDELEGATECALL, extdelegatecall) \ + ON_OPCODE_IDENTIFIER(OP_STATICCALL, staticcall) \ + ON_OPCODE_IDENTIFIER(OP_EXTSTATICCALL, extstaticcall) \ + ON_OPCODE_UNDEFINED(0xfc) \ + ON_OPCODE_IDENTIFIER(OP_REVERT, revert) \ + ON_OPCODE_IDENTIFIER(OP_INVALID, invalid) \ ON_OPCODE_IDENTIFIER(OP_SELFDESTRUCT, selfdestruct) diff --git a/lib/evmone/opcodes_helpers.h b/lib/evmone/opcodes_helpers.h deleted file mode 100644 index 68f97ee4..00000000 --- a/lib/evmone/opcodes_helpers.h +++ /dev/null @@ -1,40 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2019 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#define ANY_SMALL_PUSH \ - OP_PUSH1: \ - case OP_PUSH2: \ - case OP_PUSH3: \ - case OP_PUSH4: \ - case OP_PUSH5: \ - case OP_PUSH6: \ - case OP_PUSH7: \ - case OP_PUSH8 - -#define ANY_LARGE_PUSH \ - OP_PUSH9: \ - case OP_PUSH10: \ - case OP_PUSH11: \ - case OP_PUSH12: \ - case OP_PUSH13: \ - case OP_PUSH14: \ - case OP_PUSH15: \ - case OP_PUSH16: \ - case OP_PUSH17: \ - case OP_PUSH18: \ - case OP_PUSH19: \ - case OP_PUSH20: \ - case OP_PUSH21: \ - case OP_PUSH22: \ - case OP_PUSH23: \ - case OP_PUSH24: \ - case OP_PUSH25: \ - case OP_PUSH26: \ - case OP_PUSH27: \ - case OP_PUSH28: \ - case OP_PUSH29: \ - case OP_PUSH30: \ - case OP_PUSH31: \ - case OP_PUSH32 diff --git a/lib/evmone/tracing.cpp b/lib/evmone/tracing.cpp index 8ef933ae..d471c257 100644 --- a/lib/evmone/tracing.cpp +++ b/lib/evmone/tracing.cpp @@ -70,10 +70,12 @@ class InstructionTracer : public Tracer { struct Context { + const int32_t depth; const uint8_t* const code; ///< Reference to the code being executed. const int64_t start_gas; - Context(const uint8_t* c, int64_t g) noexcept : code{c}, start_gas{g} {} + Context(int32_t d, const uint8_t* c, int64_t g) noexcept : depth{d}, code{c}, start_gas{g} + {} }; std::stack m_contexts; @@ -94,15 +96,9 @@ class InstructionTracer : public Tracer } void on_execution_start( - evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept override + evmc_revision /*rev*/, const evmc_message& msg, bytes_view code) noexcept override { - m_contexts.emplace(code.data(), msg.gas); - - m_out << "{"; - m_out << R"("depth":)" << msg.depth; - m_out << R"(,"rev":")" << rev << '"'; - m_out << R"(,"static":)" << (((msg.flags & EVMC_STATIC) != 0) ? "true" : "false"); - m_out << "}\n"; + m_contexts.emplace(msg.depth, code.data(), msg.gas); } void on_instruction_start(uint32_t pc, const intx::uint256* stack_top, int stack_height, @@ -114,35 +110,36 @@ class InstructionTracer : public Tracer m_out << "{"; m_out << R"("pc":)" << std::dec << pc; m_out << R"(,"op":)" << std::dec << int{opcode}; - m_out << R"(,"opName":")" << get_name(opcode) << '"'; - m_out << R"(,"gas":0x)" << std::hex << gas; - output_stack(stack_top, stack_height); + m_out << R"(,"gas":"0x)" << std::hex << gas << '"'; + + if(opcode == OP_CREATE) { + int64_t gas_cost = 32000; + if(state.eos_evm_version >= 3) { + gas_cost = state.gas_state.calc_apply_storage_gas_delta(static_cast(state.gas_params.G_txcreate)); + } else if (state.eos_evm_version >= 1) { + gas_cost = static_cast(state.gas_params.G_txcreate); + } + m_out << R"(,"gasCost":"0x)" << std::hex << gas_cost << '"'; + } else { + m_out << R"(,"gasCost":"0x)" << std::hex << instr::gas_costs[state.rev][opcode] << '"'; + } // Full memory can be dumped as evmc::hex({state.memory.data(), state.memory.size()}), // but this should not be done by default. Adding --tracing=+memory option would be nice. - m_out << R"(,"memorySize":)" << std::dec << state.memory.size(); + m_out << R"(,"memSize":)" << std::dec << state.memory.size(); - m_out << "}\n"; - } - - void on_execution_end(const evmc_result& result) noexcept override - { - const auto& ctx = m_contexts.top(); + output_stack(stack_top, stack_height); + if (!state.return_data.empty()) + m_out << R"(,"returnData":"0x)" << evmc::hex(state.return_data) << '"'; + m_out << R"(,"depth":)" << std::dec << (ctx.depth + 1); + m_out << R"(,"refund":)" << std::dec << state.gas_refund; + m_out << R"(,"opName":")" << get_name(opcode) << '"'; - m_out << "{"; - m_out << R"("error":)"; - if (result.status_code == EVMC_SUCCESS) - m_out << "null"; - else - m_out << '"' << result.status_code << '"'; - m_out << R"(,"gas":)" << std::hex << "0x" << result.gas_left; - m_out << R"(,"gasUsed":)" << std::hex << "0x" << (ctx.start_gas - result.gas_left); - m_out << R"(,"output":")" << evmc::hex({result.output_data, result.output_size}) << '"'; m_out << "}\n"; - - m_contexts.pop(); } + void on_execution_end(const evmc_result& /*result*/) noexcept override { m_contexts.pop(); } + public: explicit InstructionTracer(std::ostream& out) noexcept : m_out{out} { diff --git a/lib/evmone/tracing.hpp b/lib/evmone/tracing.hpp index 3d923dcc..16a0757a 100644 --- a/lib/evmone/tracing.hpp +++ b/lib/evmone/tracing.hpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include #include @@ -12,8 +13,7 @@ namespace evmone { -using bytes_view = std::basic_string_view; - +using evmc::bytes_view; class ExecutionState; class Tracer diff --git a/lib/evmone/vm.cpp b/lib/evmone/vm.cpp index 9b7ea8a1..aa15e891 100644 --- a/lib/evmone/vm.cpp +++ b/lib/evmone/vm.cpp @@ -45,7 +45,7 @@ evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const* c_vm->execute = evmone::advanced::execute; return EVMC_SET_OPTION_SUCCESS; } - else + else #endif if (name == "cgoto") { @@ -63,14 +63,14 @@ evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const* else if (name == "trace") { #if not defined(ANTELOPE) - vm.add_tracer(create_instruction_tracer(std::cerr)); + vm.add_tracer(create_instruction_tracer(std::clog)); return EVMC_SET_OPTION_SUCCESS; #endif } else if (name == "histogram") { #if not defined(ANTELOPE) - vm.add_tracer(create_histogram_tracer(std::cerr)); + vm.add_tracer(create_histogram_tracer(std::clog)); return EVMC_SET_OPTION_SUCCESS; #endif } @@ -80,7 +80,7 @@ evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const* } // namespace -inline constexpr VM::VM() noexcept +VM::VM() noexcept : evmc_vm{ EVMC_ABI_VERSION, "evmone", diff --git a/lib/evmone/vm.hpp b/lib/evmone/vm.hpp index 98c8d391..5777c6fa 100644 --- a/lib/evmone/vm.hpp +++ b/lib/evmone/vm.hpp @@ -3,8 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "execution_state.hpp" #include "tracing.hpp" #include +#include #if defined(_MSC_VER) && !defined(__clang__) #define EVMONE_CGOTO_SUPPORTED 0 @@ -19,12 +21,13 @@ class VM : public evmc_vm { public: bool cgoto = EVMONE_CGOTO_SUPPORTED; + bool validate_eof = false; private: std::unique_ptr m_first_tracer; public: - inline constexpr VM() noexcept; + VM() noexcept; void add_tracer(std::unique_ptr tracer) noexcept { diff --git a/lib/evmone_precompiles/CMakeLists.txt b/lib/evmone_precompiles/CMakeLists.txt new file mode 100644 index 00000000..b2259dfa --- /dev/null +++ b/lib/evmone_precompiles/CMakeLists.txt @@ -0,0 +1,27 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +include(blst) + +add_library(evmone_precompiles STATIC) +add_library(evmone::precompiles ALIAS evmone_precompiles) +target_link_libraries(evmone_precompiles PUBLIC evmc::evmc_cpp PRIVATE evmone::evmmax blst::blst) +target_sources( + evmone_precompiles PRIVATE + blake2b.hpp + blake2b.cpp + bls.hpp + bls.cpp + bn254.hpp + bn254.cpp + ecc.hpp + ripemd160.hpp + ripemd160.cpp + secp256k1.hpp + secp256k1.cpp + sha256.hpp + sha256.cpp + kzg.hpp + kzg.cpp +) diff --git a/lib/evmone_precompiles/blake2b.cpp b/lib/evmone_precompiles/blake2b.cpp new file mode 100644 index 00000000..60ef1aa6 --- /dev/null +++ b/lib/evmone_precompiles/blake2b.cpp @@ -0,0 +1,74 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "blake2b.hpp" +#include +#include + +namespace evmone::crypto +{ +void blake2b_compress( + uint32_t rounds, uint64_t h[8], const uint64_t m[16], const uint64_t t[2], bool last) noexcept +{ + // Message Schedule SIGMA. + // https://datatracker.ietf.org/doc/html/rfc7693#section-2.7 + static constexpr uint8_t sigma[10][16]{ + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + }; + + // Mixing Function G. + // https://datatracker.ietf.org/doc/html/rfc7693#section-3.1 + // + // The G primitive function mixes two input words, "x" and "y", into + // four words indexed by "a", "b", "c", and "d" in the working vector v[0..15]. + static constexpr auto g = [](uint64_t v[16], size_t a, size_t b, size_t c, size_t d, uint64_t x, + uint64_t y) noexcept { + v[a] = v[a] + v[b] + x; + v[d] = std::rotr(v[d] ^ v[a], 32); + v[c] = v[c] + v[d]; + v[b] = std::rotr(v[b] ^ v[c], 24); + v[a] = v[a] + v[b] + y; + v[d] = std::rotr(v[d] ^ v[a], 16); + v[c] = v[c] + v[d]; + v[b] = std::rotr(v[b] ^ v[c], 63); + }; + + // Initialize local work vector v[0..15]. + uint64_t v[16]{h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], // First half from state. + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, // Second half from IV. + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1 ^ t[0], // Low word of the offset. + 0x9b05688c2b3e6c1f ^ t[1], // High word. + 0x1f83d9abfb41bd6b ^ (0 - uint64_t{last}), // Last block flag? Invert all bits. + 0x5be0cd19137e2179}; + + // Cryptographic mixing. + for (size_t i = 0; i < rounds; ++i) + { + // Message word selection permutation for this round. + const auto& s = sigma[i % std::size(sigma)]; + + g(v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + g(v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for (size_t i = 0; i < 8; ++i) // XOR the two halves. + h[i] ^= v[i] ^ v[i + 8]; +} +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/blake2b.hpp b/lib/evmone_precompiles/blake2b.hpp new file mode 100644 index 00000000..36dc282c --- /dev/null +++ b/lib/evmone_precompiles/blake2b.hpp @@ -0,0 +1,20 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include + +namespace evmone::crypto +{ +/// BLAKE2b compress function F. +/// https://datatracker.ietf.org/doc/html/rfc7693#section-3.2 +/// +/// @param rounds the number of rounds to perform +/// @param[in,out] h the state vector +/// @param m the block vector +/// @param t the 128-bit offset counter, {low word, high word} +/// @param last the final block indicator flag "f" +void blake2b_compress( + uint32_t rounds, uint64_t h[8], const uint64_t m[16], const uint64_t t[2], bool last) noexcept; +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/bls.cpp b/lib/evmone_precompiles/bls.cpp new file mode 100644 index 00000000..9a377c44 --- /dev/null +++ b/lib/evmone_precompiles/bls.cpp @@ -0,0 +1,418 @@ +#include "bls.hpp" +#include +#include +#include +#include + +namespace evmone::crypto::bls +{ +namespace +{ +/// Offset of the beginning of field element. First 16 bytes must be zero according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#field-elements-encoding +constexpr auto FP_BYTES_OFFSET = 64 - 48; + +/// Validates that integer encoded in big endian is valid element of BLS12-381 Fp field +[[nodiscard]] std::optional validate_fp(const uint8_t _p[64]) noexcept +{ + if (intx::be::unsafe::load(_p) >= BLS_FIELD_MODULUS) + return std::nullopt; + + blst_fp p; + blst_fp_from_bendian(&p, &_p[FP_BYTES_OFFSET]); + return p; +} + +/// Validates p1 affine point. Checks that point coordinates are from the BLS12-381 field and +/// that the point is on curve. https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-addition +[[nodiscard]] std::optional validate_p1( + const uint8_t _x[64], const uint8_t _y[64]) noexcept +{ + const auto x = validate_fp(_x); + if (!x.has_value()) + return std::nullopt; + const auto y = validate_fp(_y); + if (!y.has_value()) + return std::nullopt; + + const blst_p1_affine p0_affine{*x, *y}; + if (!blst_p1_affine_on_curve(&p0_affine)) + return std::nullopt; + + return p0_affine; +} + +/// Validates that integer encoded in big endian is valid element of BLS12-381 Fp2 extension field +[[nodiscard]] std::optional validate_fp2(const uint8_t _p[128]) noexcept +{ + const auto fp0 = validate_fp(_p); + if (!fp0.has_value()) + return std::nullopt; + const auto fp1 = validate_fp(&_p[64]); + if (!fp1.has_value()) + return std::nullopt; + + return {{*fp0, *fp1}}; +} + +/// Validates p2 affine point. Checks that point coordinates are from the BLS12-381 field and +/// that the point is on curve. https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-addition +[[nodiscard]] std::optional validate_p2( + const uint8_t _x[128], const uint8_t _y[128]) noexcept +{ + const auto x = validate_fp2(_x); + if (!x.has_value()) + return std::nullopt; + + const auto y = validate_fp2(_y); + if (!y.has_value()) + return std::nullopt; + + const blst_p2_affine p_affine{*x, *y}; + if (!blst_p2_affine_on_curve(&p_affine)) + return std::nullopt; + + return p_affine; +} + +/// Stores fp in 64-bytes array with big endian encoding zero padded. +void store(uint8_t _rx[64], const blst_fp& _x) noexcept +{ + std::memset(_rx, 0, FP_BYTES_OFFSET); + blst_bendian_from_fp(&_rx[FP_BYTES_OFFSET], &_x); +} + +/// Stores fp2 in 128-bytes array with big endian encoding zero padded. +void store(uint8_t _rx[128], const blst_fp2& _x) noexcept +{ + store(_rx, _x.fp[0]); + store(&_rx[64], _x.fp[1]); +} + +} // namespace + +[[nodiscard]] bool g1_add(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x0[64], + const uint8_t _y0[64], const uint8_t _x1[64], const uint8_t _y1[64]) noexcept +{ + const auto p0_affine = validate_p1(_x0, _y0); + const auto p1_affine = validate_p1(_x1, _y1); + + if (!p0_affine.has_value() || !p1_affine.has_value()) + return false; + + blst_p1 p0; + blst_p1_from_affine(&p0, &*p0_affine); + + blst_p1 out; + blst_p1_add_or_double_affine(&out, &p0, &*p1_affine); + + blst_p1_affine result; + blst_p1_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g1_mul(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x[64], + const uint8_t _y[64], const uint8_t _c[32]) noexcept +{ + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, _c); + + const auto p_affine = validate_p1(_x, _y); + if (!p_affine.has_value()) + return false; + + blst_p1 p; + blst_p1_from_affine(&p, &*p_affine); + + if (!blst_p1_in_g1(&p)) + return false; + + blst_p1 out; + blst_p1_mult(&out, &p, scalar.b, 256); + + blst_p1_affine result; + blst_p1_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g2_add(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x0[128], + const uint8_t _y0[128], const uint8_t _x1[128], const uint8_t _y1[128]) noexcept +{ + const auto p0_affine = validate_p2(_x0, _y0); + const auto p1_affine = validate_p2(_x1, _y1); + + if (!p0_affine.has_value() || !p1_affine.has_value()) + return false; + + blst_p2 p0; + blst_p2_from_affine(&p0, &*p0_affine); + + blst_p2 out; + blst_p2_add_or_double_affine(&out, &p0, &*p1_affine); + + blst_p2_affine result; + blst_p2_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g2_mul(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x[128], + const uint8_t _y[128], const uint8_t _c[32]) noexcept +{ + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, _c); + + const auto p_affine = validate_p2(_x, _y); + if (!p_affine.has_value()) + return false; + + blst_p2 p; + blst_p2_from_affine(&p, &*p_affine); + + if (!blst_p2_in_g2(&p)) + return false; + + blst_p2 out; + blst_p2_mult(&out, &p, scalar.b, 256); + + blst_p2_affine result; + blst_p2_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g1_msm( + uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept +{ + constexpr auto SINGLE_ENTRY_SIZE = (64 * 2 + 32); + assert(size % SINGLE_ENTRY_SIZE == 0); + auto npoints = size / SINGLE_ENTRY_SIZE; + + std::vector p1_affines; + std::vector p1_affine_ptrs; + p1_affines.reserve(npoints); + p1_affine_ptrs.reserve(npoints); + + std::vector scalars; + std::vector scalars_ptrs; + scalars.reserve(npoints); + scalars_ptrs.reserve(npoints); + + auto ptr = _xycs; + for (size_t i = 0; i < npoints; ++i) + { + const auto p_affine = validate_p1(ptr, &ptr[64]); + if (!p_affine.has_value()) + return false; + + if (!blst_p1_affine_in_g1(&*p_affine)) + return false; + + // Point at infinity must be filtered out for BLST library. + if (blst_p1_affine_is_inf(&*p_affine)) + continue; + + const auto& p = p1_affines.emplace_back(*p_affine); + p1_affine_ptrs.emplace_back(&p); + + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, &ptr[128]); + const auto& s = scalars.emplace_back(scalar); + scalars_ptrs.emplace_back(s.b); + + ptr += SINGLE_ENTRY_SIZE; + } + + npoints = p1_affine_ptrs.size(); + + if (npoints == 0) + { + memset(_rx, 0, 64); + memset(_ry, 0, 64); + return true; + } + + const auto scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t); + const auto scratch_space = std::make_unique_for_overwrite(scratch_size); + blst_p1 out; + blst_p1s_mult_pippenger( + &out, p1_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get()); + + blst_p1_affine result; + blst_p1_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool g2_msm( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept +{ + constexpr auto SINGLE_ENTRY_SIZE = (128 * 2 + 32); + assert(size % SINGLE_ENTRY_SIZE == 0); + auto npoints = size / SINGLE_ENTRY_SIZE; + + std::vector p2_affines; + std::vector p2_affine_ptrs; + p2_affines.reserve(npoints); + p2_affine_ptrs.reserve(npoints); + + std::vector scalars; + std::vector scalars_ptrs; + scalars.reserve(npoints); + scalars_ptrs.reserve(npoints); + + auto ptr = _xycs; + for (size_t i = 0; i < npoints; ++i) + { + const auto p_affine = validate_p2(ptr, &ptr[128]); + if (!p_affine.has_value()) + return false; + + if (!blst_p2_affine_in_g2(&*p_affine)) + return false; + + // Point at infinity must be filtered out for BLST library. + if (blst_p2_affine_is_inf(&*p_affine)) + continue; + + const auto& p = p2_affines.emplace_back(*p_affine); + p2_affine_ptrs.emplace_back(&p); + + blst_scalar scalar; + blst_scalar_from_bendian(&scalar, &ptr[256]); + const auto& s = scalars.emplace_back(scalar); + scalars_ptrs.emplace_back(s.b); + + ptr += SINGLE_ENTRY_SIZE; + } + + npoints = p2_affine_ptrs.size(); + + if (npoints == 0) + { + memset(_rx, 0, 128); + memset(_ry, 0, 128); + return true; + } + + const auto scratch_size = blst_p2s_mult_pippenger_scratch_sizeof(npoints) / sizeof(limb_t); + const auto scratch_space = std::make_unique_for_overwrite(scratch_size); + blst_p2 out; + blst_p2s_mult_pippenger( + &out, p2_affine_ptrs.data(), npoints, scalars_ptrs.data(), 256, scratch_space.get()); + + blst_p2_affine result; + blst_p2_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool map_fp_to_g1(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _fp[64]) noexcept +{ + const auto fp = validate_fp(_fp); + if (!fp.has_value()) + return false; + + blst_p1 out; + blst_map_to_g1(&out, &*fp); + + blst_p1_affine result; + blst_p1_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool map_fp2_to_g2( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t _fp2[128]) noexcept +{ + const auto fp2 = validate_fp2(_fp2); + if (!fp2.has_value()) + return false; + + blst_p2 out; + blst_map_to_g2(&out, &*fp2); + + blst_p2_affine result; + blst_p2_to_affine(&result, &out); + store(_rx, result.x); + store(_ry, result.y); + + return true; +} + +[[nodiscard]] bool pairing_check(uint8_t _r[32], const uint8_t* _pairs, size_t size) noexcept +{ + static constexpr auto FP_SIZE = 64; + static constexpr auto FP2_SIZE = 2 * FP_SIZE; + static constexpr auto P1_SIZE = 2 * FP_SIZE; + static constexpr auto P2_SIZE = 2 * FP2_SIZE; + static constexpr auto PAIR_SIZE = P1_SIZE + P2_SIZE; + assert(size % PAIR_SIZE == 0); + + const auto npairs = size / PAIR_SIZE; + auto ptr = _pairs; + auto has_inf = false; + blst_fp12 acc = *blst_fp12_one(); + + const auto Qlines = std::make_unique_for_overwrite(68); + + for (size_t i = 0; i < npairs; ++i) + { + const auto P_affine = validate_p1(ptr, &ptr[FP_SIZE]); + if (!P_affine.has_value()) + return false; + + const auto Q_affine = validate_p2(&ptr[P1_SIZE], &ptr[P1_SIZE + FP2_SIZE]); + if (!Q_affine.has_value()) + return false; + + if (!blst_p1_affine_in_g1(&*P_affine)) + return false; + + if (!blst_p2_affine_in_g2(&*Q_affine)) + return false; + + if (blst_p1_affine_is_inf(&*P_affine) || blst_p2_affine_is_inf(&*Q_affine)) + has_inf = true; + + if (!has_inf) + { + blst_precompute_lines(Qlines.get(), &*Q_affine); + + blst_fp12 ml_res; + blst_miller_loop_lines(&ml_res, Qlines.get(), &*P_affine); + blst_fp12_mul(&acc, &acc, &ml_res); + } + + ptr += PAIR_SIZE; + } + + bool result = true; + if (!has_inf) // if any point-at-infinity encountered the answer is 1, so skip final exp. + { + blst_final_exp(&acc, &acc); + result = blst_fp12_is_one(&acc); + } + + std::memset(_r, 0, 31); + _r[31] = result ? 1 : 0; + return true; +} + +} // namespace evmone::crypto::bls diff --git a/lib/evmone_precompiles/bls.hpp b/lib/evmone_precompiles/bls.hpp new file mode 100644 index 00000000..eac6b4ad --- /dev/null +++ b/lib/evmone_precompiles/bls.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include + +using namespace intx::literals; + +namespace evmone::crypto::bls +{ +/// The BLS12-381 field prime number. +inline constexpr auto BLS_FIELD_MODULUS = + 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab_u384; + +/// Addition in BLS12-381 curve group. +/// +/// Computes P ⊕ Q for two points in affine coordinates on the BLS12-381 curve, +[[nodiscard]] bool g1_add(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x0[64], + const uint8_t _y0[64], const uint8_t _x1[64], const uint8_t _y1[64]) noexcept; + +/// Scalar multiplication in BLS12-381 curve G1 subgroup. +/// +/// Computes [c]P for a point in affine coordinate on the BLS12-381 curve, performs subgroup check +/// according to spec https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-multiplication +[[nodiscard]] bool g1_mul(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _x[64], + const uint8_t _y[64], const uint8_t _c[32]) noexcept; + +/// Addition in BLS12-381 curve group over G2 extension field. +/// +/// Computes P ⊕ Q for two points in affine coordinates on the BLS12-381 curve over G2 extension +/// field, performs subgroup checks for both points according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-addition +[[nodiscard]] bool g2_add(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x0[128], + const uint8_t _y0[128], const uint8_t _x1[128], const uint8_t _y1[128]) noexcept; + +/// Scalar multiplication in BLS12-381 curve group over G2 extension field +/// +/// Computes [c]P for a point in affine coordinate on the BLS12-381 curve over G2 extension +/// field, performs subgroup check according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-multiplication +[[nodiscard]] bool g2_mul(uint8_t _rx[128], uint8_t _ry[128], const uint8_t _x[128], + const uint8_t _y[128], const uint8_t _c[32]) noexcept; + +/// Multi scalar multiplication in BLS12-381 curve G1 subgroup. +/// +/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve, performs +/// subgroup check according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-msm +[[nodiscard]] bool g1_msm( + uint8_t _rx[64], uint8_t _ry[64], const uint8_t* _xycs, size_t size) noexcept; + +/// Multi scalar multiplication in BLS12-381 curve G2 subgroup. +/// +/// Computes ∑ⁿₖ₌₁cₖPₖ for points in affine coordinate on the BLS12-381 curve over G2 extension +/// field, performs subgroup check according to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-msm +[[nodiscard]] bool g2_msm( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t* _xycs, size_t size) noexcept; + +/// Maps field element of Fp to curve point on BLS12-381 curve G1 subgroup. +/// +/// Performs field Fp element check. Returns `false` if an element is not from the field. +/// According to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-mapping-fp-element-to-g1-point +[[nodiscard]] bool map_fp_to_g1(uint8_t _rx[64], uint8_t _ry[64], const uint8_t _fp[64]) noexcept; + +/// Maps field element of Fp2 to curve point on BLS12-381 curve G2 subgroup. +/// +/// Performs field Fp2 element check. Returns `false` if an element is not from the field. +/// According to spec +/// https://eips.ethereum.org/EIPS/eip-2537#abi-for-mapping-fp2-element-to-g2-point +[[nodiscard]] bool map_fp2_to_g2( + uint8_t _rx[128], uint8_t _ry[128], const uint8_t _fp[128]) noexcept; + +/// Computes pairing for pairs of P and Q point from G1 and G2 accordingly. +/// +/// Performs filed and groups check for both input points. Returns 'false' if any of requirement is +/// not met according to spec https://eips.ethereum.org/EIPS/eip-2537#abi-for-pairing-check +[[nodiscard]] bool pairing_check(uint8_t _r[32], const uint8_t* _pairs, size_t size) noexcept; + +} // namespace evmone::crypto::bls diff --git a/lib/evmone_precompiles/bn254.cpp b/lib/evmone_precompiles/bn254.cpp new file mode 100644 index 00000000..79c4e141 --- /dev/null +++ b/lib/evmone_precompiles/bn254.cpp @@ -0,0 +1,420 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "bn254.hpp" + +namespace evmmax::bn254 +{ +namespace +{ +constexpr ModArith Fp{FieldPrime}; +constexpr auto B = Fp.to_mont(3); +constexpr auto B3 = Fp.to_mont(3 * 3); +} // namespace + +bool validate(const Point& pt) noexcept +{ + if (pt.is_inf()) + return true; + + const auto xm = Fp.to_mont(pt.x); + const auto ym = Fp.to_mont(pt.y); + const auto y2 = Fp.mul(ym, ym); + const auto x2 = Fp.mul(xm, xm); + const auto x3 = Fp.mul(x2, xm); + const auto x3_3 = Fp.add(x3, B); + return y2 == x3_3; +} + +Point add(const Point& pt1, const Point& pt2) noexcept +{ + if (pt1.is_inf()) + return pt2; + if (pt2.is_inf()) + return pt1; + + // b3 == 9 for y^2 == x^3 + 3 + const auto r = ecc::add(Fp, ecc::to_proj(Fp, pt1), ecc::to_proj(Fp, pt2), B3); + + return ecc::to_affine(Fp, field_inv, r); +} + +Point mul(const Point& pt, const uint256& c) noexcept +{ + if (pt.is_inf()) + return pt; + + if (c == 0) + return {}; + + const auto pr = ecc::mul(Fp, ecc::to_proj(Fp, pt), c, B3); + + return ecc::to_affine(Fp, field_inv, pr); +} + +uint256 field_inv(const ModArith& m, const uint256& x) noexcept +{ + // Computes modular exponentiation + // x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 + // Operations: 247 squares 56 multiplies + // Generated by github.com/mmcloughlin/addchain v0.4.0. + // addchain search 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 + // > bn254_field_inv.acc + // addchain gen -tmpl expmod.tmpl bn254_field_inv.acc + // > bn254_field_inv.cpp + // + // Inversion computation is derived from the addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _110 = 1 + _101 + // _1000 = _10 + _110 + // _1101 = _101 + _1000 + // _10010 = _101 + _1101 + // _10011 = 1 + _10010 + // _10100 = 1 + _10011 + // _10111 = _11 + _10100 + // _11100 = _101 + _10111 + // _100000 = _1101 + _10011 + // _100011 = _11 + _100000 + // _101011 = _1000 + _100011 + // _101111 = _10011 + _11100 + // _1000001 = _10010 + _101111 + // _1010011 = _10010 + _1000001 + // _1011011 = _1000 + _1010011 + // _1100001 = _110 + _1011011 + // _1110101 = _10100 + _1100001 + // _10010001 = _11100 + _1110101 + // _10010101 = _100000 + _1110101 + // _10110101 = _100000 + _10010101 + // _10111011 = _110 + _10110101 + // _11000001 = _110 + _10111011 + // _11000011 = _10 + _11000001 + // _11010011 = _10010 + _11000001 + // _11100001 = _100000 + _11000001 + // _11100011 = _10 + _11100001 + // _11100111 = _110 + _11100001 + // i57 = ((_11000001 << 8 + _10010001) << 10 + _11100111) << 7 + // i76 = ((_10111 + i57) << 9 + _10011) << 7 + _1101 + // i109 = ((i76 << 14 + _1010011) << 9 + _11100001) << 8 + // i127 = ((_1000001 + i109) << 10 + _1011011) << 5 + _1101 + // i161 = ((i127 << 8 + _11) << 12 + _101011) << 12 + // i186 = ((_10111011 + i161) << 8 + _101111) << 14 + _10110101 + // i214 = ((i186 << 9 + _10010001) << 5 + _1101) << 12 + // i236 = ((_11100011 + i214) << 8 + _10010101) << 11 + _11010011 + // i268 = ((i236 << 7 + _1100001) << 11 + _100011) << 12 + // i288 = ((_1011011 + i268) << 9 + _11000011) << 8 + _11100111 + // return (i288 << 7 + _1110101) << 6 + _101 + // + // Operations: 247 squares 56 m.multiplies + // + // Generated by github.com/mmcloughlin/addchain v0.4.0. + + // Allocate Temporaries. + uint256 t0; + uint256 t1; + uint256 t2; + uint256 t3; + uint256 t4; + uint256 t5; + uint256 t6; + uint256 t7; + uint256 t8; + uint256 t9; + uint256 t10; + uint256 t11; + uint256 t12; + uint256 t13; + uint256 t14; + uint256 t15; + uint256 t16; + uint256 t17; + uint256 t18; + uint256 t19; + uint256 t20; + uint256 t21; + // Step 1: t8 = x^0x2 + t8 = m.mul(x, x); + + // Step 2: t15 = x^0x3 + t15 = m.mul(x, t8); + + // Step 3: z = x^0x5 + auto z = m.mul(t8, t15); + + // Step 4: t1 = x^0x6 + t1 = m.mul(x, z); + + // Step 5: t3 = x^0x8 + t3 = m.mul(t8, t1); + + // Step 6: t9 = x^0xd + t9 = m.mul(z, t3); + + // Step 7: t6 = x^0x12 + t6 = m.mul(z, t9); + + // Step 8: t19 = x^0x13 + t19 = m.mul(x, t6); + + // Step 9: t0 = x^0x14 + t0 = m.mul(x, t19); + + // Step 10: t20 = x^0x17 + t20 = m.mul(t15, t0); + + // Step 11: t2 = x^0x1c + t2 = m.mul(z, t20); + + // Step 12: t17 = x^0x20 + t17 = m.mul(t9, t19); + + // Step 13: t4 = x^0x23 + t4 = m.mul(t15, t17); + + // Step 14: t14 = x^0x2b + t14 = m.mul(t3, t4); + + // Step 15: t12 = x^0x2f + t12 = m.mul(t19, t2); + + // Step 16: t16 = x^0x41 + t16 = m.mul(t6, t12); + + // Step 17: t18 = x^0x53 + t18 = m.mul(t6, t16); + + // Step 18: t3 = x^0x5b + t3 = m.mul(t3, t18); + + // Step 19: t5 = x^0x61 + t5 = m.mul(t1, t3); + + // Step 20: t0 = x^0x75 + t0 = m.mul(t0, t5); + + // Step 21: t10 = x^0x91 + t10 = m.mul(t2, t0); + + // Step 22: t7 = x^0x95 + t7 = m.mul(t17, t0); + + // Step 23: t11 = x^0xb5 + t11 = m.mul(t17, t7); + + // Step 24: t13 = x^0xbb + t13 = m.mul(t1, t11); + + // Step 25: t21 = x^0xc1 + t21 = m.mul(t1, t13); + + // Step 26: t2 = x^0xc3 + t2 = m.mul(t8, t21); + + // Step 27: t6 = x^0xd3 + t6 = m.mul(t6, t21); + + // Step 28: t17 = x^0xe1 + t17 = m.mul(t17, t21); + + // Step 29: t8 = x^0xe3 + t8 = m.mul(t8, t17); + + // Step 30: t1 = x^0xe7 + t1 = m.mul(t1, t17); + + // Step 38: t21 = x^0xc100 + for (int i = 0; i < 8; ++i) + t21 = m.mul(t21, t21); + + // Step 39: t21 = x^0xc191 + t21 = m.mul(t10, t21); + + // Step 49: t21 = x^0x3064400 + for (int i = 0; i < 10; ++i) + t21 = m.mul(t21, t21); + + // Step 50: t21 = x^0x30644e7 + t21 = m.mul(t1, t21); + + // Step 57: t21 = x^0x183227380 + for (int i = 0; i < 7; ++i) + t21 = m.mul(t21, t21); + + // Step 58: t20 = x^0x183227397 + t20 = m.mul(t20, t21); + + // Step 67: t20 = x^0x30644e72e00 + for (int i = 0; i < 9; ++i) + t20 = m.mul(t20, t20); + + // Step 68: t19 = x^0x30644e72e13 + t19 = m.mul(t19, t20); + + // Step 75: t19 = x^0x1832273970980 + for (int i = 0; i < 7; ++i) + t19 = m.mul(t19, t19); + + // Step 76: t19 = x^0x183227397098d + t19 = m.mul(t9, t19); + + // Step 90: t19 = x^0x60c89ce5c2634000 + for (int i = 0; i < 14; ++i) + t19 = m.mul(t19, t19); + + // Step 91: t18 = x^0x60c89ce5c2634053 + t18 = m.mul(t18, t19); + + // Step 100: t18 = x^0xc19139cb84c680a600 + for (int i = 0; i < 9; ++i) + t18 = m.mul(t18, t18); + + // Step 101: t17 = x^0xc19139cb84c680a6e1 + t17 = m.mul(t17, t18); + + // Step 109: t17 = x^0xc19139cb84c680a6e100 + for (int i = 0; i < 8; ++i) + t17 = m.mul(t17, t17); + + // Step 110: t16 = x^0xc19139cb84c680a6e141 + t16 = m.mul(t16, t17); + + // Step 120: t16 = x^0x30644e72e131a029b850400 + for (int i = 0; i < 10; ++i) + t16 = m.mul(t16, t16); + + // Step 121: t16 = x^0x30644e72e131a029b85045b + t16 = m.mul(t3, t16); + + // Step 126: t16 = x^0x60c89ce5c263405370a08b60 + for (int i = 0; i < 5; ++i) + t16 = m.mul(t16, t16); + + // Step 127: t16 = x^0x60c89ce5c263405370a08b6d + t16 = m.mul(t9, t16); + + // Step 135: t16 = x^0x60c89ce5c263405370a08b6d00 + for (int i = 0; i < 8; ++i) + t16 = m.mul(t16, t16); + + // Step 136: t15 = x^0x60c89ce5c263405370a08b6d03 + t15 = m.mul(t15, t16); + + // Step 148: t15 = x^0x60c89ce5c263405370a08b6d03000 + for (int i = 0; i < 12; ++i) + t15 = m.mul(t15, t15); + + // Step 149: t14 = x^0x60c89ce5c263405370a08b6d0302b + t14 = m.mul(t14, t15); + + // Step 161: t14 = x^0x60c89ce5c263405370a08b6d0302b000 + for (int i = 0; i < 12; ++i) + t14 = m.mul(t14, t14); + + // Step 162: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb + t13 = m.mul(t13, t14); + + // Step 170: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb00 + for (int i = 0; i < 8; ++i) + t13 = m.mul(t13, t13); + + // Step 171: t12 = x^0x60c89ce5c263405370a08b6d0302b0bb2f + t12 = m.mul(t12, t13); + + // Step 185: t12 = x^0x183227397098d014dc2822db40c0ac2ecbc000 + for (int i = 0; i < 14; ++i) + t12 = m.mul(t12, t12); + + // Step 186: t11 = x^0x183227397098d014dc2822db40c0ac2ecbc0b5 + t11 = m.mul(t11, t12); + + // Step 195: t11 = x^0x30644e72e131a029b85045b68181585d97816a00 + for (int i = 0; i < 9; ++i) + t11 = m.mul(t11, t11); + + // Step 196: t10 = x^0x30644e72e131a029b85045b68181585d97816a91 + t10 = m.mul(t10, t11); + + // Step 201: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d5220 + for (int i = 0; i < 5; ++i) + t10 = m.mul(t10, t10); + + // Step 202: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d + t9 = m.mul(t9, t10); + + // Step 214: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d000 + for (int i = 0; i < 12; ++i) + t9 = m.mul(t9, t9); + + // Step 215: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3 + t8 = m.mul(t8, t9); + + // Step 223: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e300 + for (int i = 0; i < 8; ++i) + t8 = m.mul(t8, t8); + + // Step 224: t7 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e395 + t7 = m.mul(t7, t8); + + // Step 235: t7 = x^0x30644e72e131a029b85045b68181585d97816a916871ca800 + for (int i = 0; i < 11; ++i) + t7 = m.mul(t7, t7); + + // Step 236: t6 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3 + t6 = m.mul(t6, t7); + + // Step 243: t6 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e546980 + for (int i = 0; i < 7; ++i) + t6 = m.mul(t6, t6); + + // Step 244: t5 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e1 + t5 = m.mul(t5, t6); + + // Step 255: t5 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0800 + for (int i = 0; i < 11; ++i) + t5 = m.mul(t5, t5); + + // Step 256: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823 + t4 = m.mul(t4, t5); + + // Step 268: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823000 + for (int i = 0; i < 12; ++i) + t4 = m.mul(t4, t4); + + // Step 269: t3 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b + t3 = m.mul(t3, t4); + + // Step 278: t3 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b600 + for (int i = 0; i < 9; ++i) + t3 = m.mul(t3, t3); + + // Step 279: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3 + t2 = m.mul(t2, t3); + + // Step 287: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c300 + for (int i = 0; i < 8; ++i) + t2 = m.mul(t2, t2); + + // Step 288: t1 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7 + t1 = m.mul(t1, t2); + + // Step 295: t1 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f380 + for (int i = 0; i < 7; ++i) + t1 = m.mul(t1, t1); + + // Step 296: t0 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5 + t0 = m.mul(t0, t1); + + // Step 302: t0 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd40 + for (int i = 0; i < 6; ++i) + t0 = m.mul(t0, t0); + + // Step 303: z = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 + z = m.mul(z, t0); + + return z; +} + +} // namespace evmmax::bn254 diff --git a/lib/evmone_precompiles/bn254.hpp b/lib/evmone_precompiles/bn254.hpp new file mode 100644 index 00000000..1f0d5f24 --- /dev/null +++ b/lib/evmone_precompiles/bn254.hpp @@ -0,0 +1,39 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "ecc.hpp" + +namespace evmmax::bn254 +{ +using namespace intx; + +/// The bn254 field prime number (P). +inline constexpr auto FieldPrime = + 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256; + +using Point = ecc::Point; + +/// Validates that point is from the bn254 curve group +/// +/// Returns true if y^2 == x^3 + 3. Input is converted to the Montgomery form. +bool validate(const Point& pt) noexcept; + +/// Modular inversion for bn254 prime field. +/// +/// Computes 1/x mod P modular inversion by computing modular exponentiation x^(P-2), +/// where P is ::FieldPrime. +uint256 field_inv(const ModArith& m, const uint256& x) noexcept; + +/// Addition in bn254 curve group. +/// +/// Computes P ⊕ Q for two points in affine coordinates on the bn254 curve, +Point add(const Point& pt1, const Point& pt2) noexcept; + +/// Scalar multiplication in bn254 curve group. +/// +/// Computes [c]P for a point in affine coordinate on the bn254 curve, +Point mul(const Point& pt, const uint256& c) noexcept; + +} // namespace evmmax::bn254 diff --git a/lib/evmone_precompiles/ecc.hpp b/lib/evmone_precompiles/ecc.hpp new file mode 100644 index 00000000..4ea616c8 --- /dev/null +++ b/lib/evmone_precompiles/ecc.hpp @@ -0,0 +1,202 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmmax::ecc +{ + +/// The affine (two coordinates) point on an Elliptic Curve over a prime field. +template +struct Point +{ + IntT x = 0; + IntT y = 0; + + friend constexpr bool operator==(const Point& a, const Point& b) noexcept = default; + + /// Checks if the point represents the special "infinity" value. + [[nodiscard]] constexpr bool is_inf() const noexcept { return *this == Point{}; } +}; + +static_assert(Point{}.is_inf()); + +template +struct ProjPoint +{ + IntT x = 0; + IntT y = 1; + IntT z = 0; + + /// Checks if the point represents the special "infinity" value. + [[nodiscard]] constexpr bool is_inf() const noexcept { return x == 0 && z == 0; } +}; + +static_assert(ProjPoint{}.is_inf()); + +template +using InvFn = IntT (*)(const ModArith&, const IntT& x) noexcept; + +/// Converts an affine point to a projected point with coordinates in Montgomery form. +template +inline ProjPoint to_proj(const ModArith& s, const Point& p) noexcept +{ + // FIXME: Add to_mont(1) to ModArith? + // FIXME: Handle inf + return {s.to_mont(p.x), s.to_mont(p.y), s.to_mont(1)}; +} + +/// Converts a projected point to an affine point. +template +inline Point to_affine( + const ModArith& s, InvFn inv, const ProjPoint& p) noexcept +{ + // FIXME: Split to_affine() and to/from_mont(). This is not good idea. + // FIXME: Add tests for inf. + const auto z_inv = inv(s, p.z); + return {s.from_mont(s.mul(p.x, z_inv)), s.from_mont(s.mul(p.y, z_inv))}; +} + +template +ProjPoint add(const evmmax::ModArith& s, const ProjPoint& p, + const ProjPoint& q, const IntT& b3) noexcept +{ + static_assert(A == 0, "point addition procedure is simplified for a = 0"); + + // Joost Renes and Craig Costello and Lejla Batina + // "Complete addition formulas for prime order elliptic curves" + // Cryptology ePrint Archive, Paper 2015/1060 + // https://eprint.iacr.org/2015/1060 + // Algorithm 7. + + const auto& x1 = p.x; + const auto& y1 = p.y; + const auto& z1 = p.z; + const auto& x2 = q.x; + const auto& y2 = q.y; + const auto& z2 = q.z; + IntT x3; + IntT y3; + IntT z3; + IntT t0; + IntT t1; + IntT t2; + IntT t3; + IntT t4; + + t0 = s.mul(x1, x2); // 1 + t1 = s.mul(y1, y2); // 2 + t2 = s.mul(z1, z2); // 3 + t3 = s.add(x1, y1); // 4 + t4 = s.add(x2, y2); // 5 + t3 = s.mul(t3, t4); // 6 + t4 = s.add(t0, t1); // 7 + t3 = s.sub(t3, t4); // 8 + t4 = s.add(y1, z1); // 9 + x3 = s.add(y2, z2); // 10 + t4 = s.mul(t4, x3); // 11 + x3 = s.add(t1, t2); // 12 + t4 = s.sub(t4, x3); // 13 + x3 = s.add(x1, z1); // 14 + y3 = s.add(x2, z2); // 15 + x3 = s.mul(x3, y3); // 16 + y3 = s.add(t0, t2); // 17 + y3 = s.sub(x3, y3); // 18 + x3 = s.add(t0, t0); // 19 + t0 = s.add(x3, t0); // 20 + t2 = s.mul(b3, t2); // 21 + z3 = s.add(t1, t2); // 22 + t1 = s.sub(t1, t2); // 23 + y3 = s.mul(b3, y3); // 24 + x3 = s.mul(t4, y3); // 25 + t2 = s.mul(t3, t1); // 26 + x3 = s.sub(t2, x3); // 27 + y3 = s.mul(y3, t0); // 28 + t1 = s.mul(t1, z3); // 29 + y3 = s.add(t1, y3); // 30 + t0 = s.mul(t0, t3); // 31 + z3 = s.mul(z3, t4); // 32 + z3 = s.add(z3, t0); // 33 + + return {x3, y3, z3}; +} + + +template +ProjPoint dbl( + const evmmax::ModArith& s, const ProjPoint& p, const IntT& b3) noexcept +{ + static_assert(A == 0, "point doubling procedure is simplified for a = 0"); + + // Joost Renes and Craig Costello and Lejla Batina + // "Complete addition formulas for prime order elliptic curves" + // Cryptology ePrint Archive, Paper 2015/1060 + // https://eprint.iacr.org/2015/1060 + // Algorithm 9. + + const auto& x = p.x; + const auto& y = p.y; + const auto& z = p.z; + IntT x3; + IntT y3; + IntT z3; + IntT t0; + IntT t1; + IntT t2; + + t0 = s.mul(y, y); // 1 + z3 = s.add(t0, t0); // 2 + z3 = s.add(z3, z3); // 3 + z3 = s.add(z3, z3); // 4 + t1 = s.mul(y, z); // 5 + t2 = s.mul(z, z); // 6 + t2 = s.mul(b3, t2); // 7 + x3 = s.mul(t2, z3); // 8 + y3 = s.add(t0, t2); // 9 + z3 = s.mul(t1, z3); // 10 + t1 = s.add(t2, t2); // 11 + t2 = s.add(t1, t2); // 12 + t0 = s.sub(t0, t2); // 13 + y3 = s.mul(t0, y3); // 14 + y3 = s.add(x3, y3); // 15 + t1 = s.mul(x, y); // 16 + x3 = s.mul(t0, t1); // 17 + x3 = s.add(x3, x3); // 18 + + return {x3, y3, z3}; +} + +template +ProjPoint mul(const evmmax::ModArith& s, const ProjPoint& z, const IntT& c, + const IntT& b3) noexcept +{ + ProjPoint p; + auto q = z; + auto first_significant_met = false; + + for (int i = 255; i >= 0; --i) + { + const auto d = c & (IntT{1} << i); + if (d == 0) + { + if (first_significant_met) + { + q = ecc::add(s, p, q, b3); + p = ecc::dbl(s, p, b3); + } + } + else + { + p = ecc::add(s, p, q, b3); + q = ecc::dbl(s, q, b3); + first_significant_met = true; + } + } + + return p; +} + + +} // namespace evmmax::ecc diff --git a/lib/evmone_precompiles/expmod.tmpl b/lib/evmone_precompiles/expmod.tmpl new file mode 100644 index 00000000..238519bd --- /dev/null +++ b/lib/evmone_precompiles/expmod.tmpl @@ -0,0 +1,44 @@ +{{- $exponent := "" -}} +{{- range $exponent = $.Chain }}{{ end -}} + +uint256 expmod_(const ModArith& m, const uint256& x) noexcept +{ + // Computes modular exponentiation + // x^{{ printf "%#x" $exponent }} + // Operations: {{ .Ops.Doubles }} squares {{ .Ops.Adds }} multiplies + // Generated by {{ .Meta.Module }} {{ .Meta.ReleaseTag }}. + // + // Exponentiation computation is derived from the addition chain: + // + {{- range lines (format .Script) }} + // {{ . }} + {{- end }} + + // Allocate Temporaries. + uint256 z; + {{- range .Program.Temporaries }} + uint256 {{ . }}; + {{- end }} + + {{ range $i := .Program.Instructions }} + // {{ printf "Step %d: %s = x^%#x" $i.Output.Index $i.Output (index $.Chain $i.Output.Index) }} + {{- with add $i.Op }} + {{ $i.Output }} = m.mul({{ .X }}, {{ .Y }}); + {{ end -}} + + {{- with double $i.Op }} + {{ $i.Output }} = m.mul({{ .X }}, {{ .X }}); + {{ end -}} + + {{- with shift $i.Op -}} + {{- $first := 0 -}} + {{- if ne $i.Output.Identifier .X.Identifier }} + {{ $i.Output }} = m.mul({{ .X }}, {{ .X }}); + {{- $first = 1 -}} + {{- end }} + for (int i = {{ $first }}; i < {{ .S }}; ++i) + {{ $i.Output }} = m.mul({{ $i.Output }}, {{ $i.Output }}); + {{ end -}} + {{- end }} + return z; +} diff --git a/lib/evmone_precompiles/kzg.cpp b/lib/evmone_precompiles/kzg.cpp new file mode 100644 index 00000000..fd211c1c --- /dev/null +++ b/lib/evmone_precompiles/kzg.cpp @@ -0,0 +1,168 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "kzg.hpp" +#include +#include +#include +#include + +namespace evmone::crypto +{ +namespace +{ +/// The field element 1 in Montgomery form. +constexpr blst_fp ONE = {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, + 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}; + +/// The negation of the subgroup G1 generator -[1]₁ (Jacobian coordinates in Montgomery form). +constexpr blst_p1 G1_GENERATOR_NEGATIVE = { + {0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, 0x120177419e0bfb75}, + {0xff526c2af318883a, 0x92899ce4383b0270, 0x89d7738d9fa9d055, 0x12caf35ba344c12a, + 0x3cff1b76964b5317, 0x0e44d2ede9774430}, + ONE}; + +/// The negation of the subgroup G2 generator -[1]₂ (Jacobian coordinates in Montgomery form). +constexpr blst_p2 G2_GENERATOR_NEGATIVE{ + {{{0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, + 0x6f67b7631863366b, 0x058191924350bcd7}, + {0xa5a9c0759e23f606, 0xaaa0c59dbccd60c3, 0x3bb17e18e2867806, 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, 0x11922a097360edf3}}}, + {{{0x6d8bf5079fb65e61, 0xc52f05df531d63a5, 0x7f4a4d344ca692c9, 0xa887959b8577c95f, + 0x4347fe40525c8734, 0x197d145bbaff0bb5}, + {0x0c3e036d209afa4e, 0x0601d8f4863f9e23, 0xe0832636bacc0a84, 0xeb2def362a476f84, + 0x64044f659f0ee1e9, 0x0ed54f48d5a1caa7}}}, + {{ONE, {}}}}; + +/// The point from the G2 series, index 1 of the Ethereum KZG trusted setup, +/// i.e. [s]₂ where s is the trusted setup's secret. +/// Affine coordinates in Montgomery form. +/// The original value in compressed form (y-parity bit and Fp2 x coordinate) +/// is the ["g2_monomial"][1] of the JSON object found at +/// https://github.com/ethereum/consensus-specs/blob/dev/presets/mainnet/trusted_setups/trusted_setup_4096.json#L8200 +constexpr blst_p2_affine KZG_SETUP_G2_1{ + {{{0x6120a2099b0379f9, 0xa2df815cb8210e4e, 0xcb57be5577bd3d4f, 0x62da0ea89a0c93f8, + 0x02e0ee16968e150d, 0x171f09aea833acd5}, + {0x11a3670749dfd455, 0x04991d7b3abffadc, 0x85446a8e14437f41, 0x27174e7b4e76e3f2, + 0x7bfa6dd397f60a20, 0x02fcc329ac07080f}}}, + {{{0xaa130838793b2317, 0xe236dd220f891637, 0x6502782925760980, 0xd05c25f60557ec89, + 0x6095767a44064474, 0x185693917080d405}, + {0x549f9e175b03dc0a, 0x32c0c95a77106cfe, 0x64a74eae5705d080, 0x53deeaf56659ed9e, + 0x09a1d368508afb93, 0x12cf3a4525b5e9bd}}}}; + +/// Load and validate an element from the group order field. +std::optional validate_scalar(std::span b) noexcept +{ + blst_scalar v; + blst_scalar_from_bendian(&v, reinterpret_cast(b.data())); + return blst_scalar_fr_check(&v) ? std::optional{v} : std::nullopt; +} + +/// Uncompress and validate a point from G1 subgroup. +std::optional validate_G1(std::span b) noexcept +{ + blst_p1_affine r; + if (blst_p1_uncompress(&r, reinterpret_cast(b.data())) != BLST_SUCCESS) + return std::nullopt; + + // Subgroup check is required by the spec but there are no test vectors + // with points outside G1 which would satisfy the final pairings check. + if (!blst_p1_affine_in_g1(&r)) + return std::nullopt; + return r; +} + +/// Add two points from E1 and convert the result to affine form. +/// The conversion to affine is very costly so use only if the affine of the result is needed. +blst_p1_affine add_or_double(const blst_p1_affine& p, const blst_p1& q) noexcept +{ + blst_p1 r; + blst_p1_add_or_double_affine(&r, &q, &p); + blst_p1_affine ra; + blst_p1_to_affine(&ra, &r); + return ra; +} + +blst_p1 mult(const blst_p1& p, const blst_scalar& v) noexcept +{ + blst_p1 r; + blst_p1_mult(&r, &p, v.b, BLS_MODULUS_BITS); + return r; +} + +/// Add two points from E2 and convert the result to affine form. +/// The conversion to affine is very costly so use only if the affine of the result is needed. +blst_p2_affine add_or_double(const blst_p2_affine& p, const blst_p2& q) noexcept +{ + blst_p2 r; + blst_p2_add_or_double_affine(&r, &q, &p); + blst_p2_affine ra; + blst_p2_to_affine(&ra, &r); + return ra; +} + +blst_p2 mult(const blst_p2& p, const blst_scalar& v) noexcept +{ + blst_p2 r; + blst_p2_mult(&r, &p, v.b, BLS_MODULUS_BITS); + return r; +} + +bool pairings_verify( + const blst_p1_affine& a1, const blst_p1_affine& b1, const blst_p2_affine& b2) noexcept +{ + blst_fp12 left; + blst_aggregated_in_g1(&left, &a1); + blst_fp12 right; + blst_miller_loop(&right, &b2, &b1); + return blst_fp12_finalverify(&left, &right); +} +} // namespace + +bool kzg_verify_proof(const std::byte versioned_hash[VERSIONED_HASH_SIZE], const std::byte z[32], + const std::byte y[32], const std::byte commitment[48], const std::byte proof[48]) noexcept +{ + std::byte computed_versioned_hash[32]; + sha256(computed_versioned_hash, commitment, 48); + computed_versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; + if (!std::ranges::equal(std::span{versioned_hash, 32}, computed_versioned_hash)) + return false; + + // Load and validate scalars z and y. + // TODO(C++26): The span construction can be done as std::snap(z, std::c_<32>). + const auto zz = validate_scalar(std::span{z, 32}); + if (!zz) + return false; + const auto yy = validate_scalar(std::span{y, 32}); + if (!yy) + return false; + + // Uncompress and validate the points C (representing the polynomial commitment) + // and Pi (representing the proof). They both are valid to be points at infinity + // when they prove a commitment to a constant polynomial, + // see https://hackmd.io/@kevaundray/kzg-is-zero-proof-sound + const auto C = validate_G1(std::span{commitment, 48}); + if (!C) + return false; + const auto Pi = validate_G1(std::span{proof, 48}); + if (!Pi) + return false; + + // Compute -Y as [y * -1]₁. + const auto neg_Y = mult(G1_GENERATOR_NEGATIVE, *yy); + + // Compute C - Y. It can happen that C == -Y so doubling may be needed. + const auto C_sub_Y = add_or_double(*C, neg_Y); + + // Compute -Z as [z * -1]₂. + const auto neg_Z = mult(G2_GENERATOR_NEGATIVE, *zz); + + // Compute X - Z which is [s - z]₂. + const auto X_sub_Z = add_or_double(KZG_SETUP_G2_1, neg_Z); + + // e(C - [y]₁, [1]₂) =? e(Pi, [s - z]₂) + return pairings_verify(C_sub_Y, *Pi, X_sub_Z); +} +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/kzg.hpp b/lib/evmone_precompiles/kzg.hpp new file mode 100644 index 00000000..02b60671 --- /dev/null +++ b/lib/evmone_precompiles/kzg.hpp @@ -0,0 +1,32 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "sha256.hpp" +#include + +namespace evmone::crypto +{ +using namespace intx::literals; + +/// Length (in bytes) of the versioned hash (based on SHA256). +constexpr auto VERSIONED_HASH_SIZE = SHA256_HASH_SIZE; + +/// The KZG version number of the versioned hash. +constexpr std::byte VERSIONED_HASH_VERSION_KZG{0x01}; + +/// An EIP-4844 parameter. +constexpr auto FIELD_ELEMENTS_PER_BLOB = 4096_u256; + +/// Scalar field modulus of BLS12-381. +constexpr auto BLS_MODULUS = + 52435875175126190479447740508185965837690552500527637822603658699938581184513_u256; + +/// Number of significant bits of the BLS_MODULUS. +constexpr size_t BLS_MODULUS_BITS = 255; +static_assert((BLS_MODULUS >> BLS_MODULUS_BITS) == 0); + +bool kzg_verify_proof(const std::byte versioned_hash[VERSIONED_HASH_SIZE], const std::byte z[32], + const std::byte y[32], const std::byte commitment[48], const std::byte proof[48]) noexcept; +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/ripemd160.cpp b/lib/evmone_precompiles/ripemd160.cpp new file mode 100644 index 00000000..23432cf4 --- /dev/null +++ b/lib/evmone_precompiles/ripemd160.cpp @@ -0,0 +1,220 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "ripemd160.hpp" +#include +#include +#include +#include +#include + +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 180000 +// libc++ before version 18 has incorrect std::rotl signature +// https://github.com/llvm/llvm-project/commit/45500fa08acdf3849de9de470cdee5f4c8ee2f32 +#pragma clang diagnostic ignored "-Wsign-conversion" +#endif + +namespace evmone::crypto +{ +namespace +{ +// TODO(C++23): Use std::byteswap. +template +constexpr T byteswap(T value) noexcept +{ + static_assert(std::has_unique_object_representations_v, "T may not have padding bits"); + auto value_representation = std::bit_cast>(value); + std::ranges::reverse(value_representation); + return std::bit_cast(value_representation); +} + +/// @file +/// The implementation of the RIPEMD-160 hash function +/// based on the "RIPEMD-160: A Strengthened Version of RIPEMD" +/// https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf + +constexpr size_t L = 2; ///< Number of lines. +constexpr size_t R = 5; ///< Number of rounds. +constexpr size_t B = 16; ///< Number of steps per round and words in a block. +constexpr size_t N = R * B; ///< Number of steps. + +using State = std::array; + +using BinaryFunction = uint32_t (*)(uint32_t, uint32_t, uint32_t) noexcept; + +// TODO: Functions from the array of function pointers are not inlined by GCC: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114452 +// TODO(C++23): Mark these as [[always_inline]] +constexpr BinaryFunction binary_functions[R] = { + // f₁(x, y, z) = x ⊕ y ⊕ z + [](uint32_t x, uint32_t y, uint32_t z) noexcept { return x ^ y ^ z; }, + + // f₂(x, y, z) = (x ∧ y) ∨ (¬x ∧ z) ⇔ ((y ⊕ z) ∧ x) ⊕ z + [](uint32_t x, uint32_t y, uint32_t z) noexcept { return ((y ^ z) & x) ^ z; }, + + // f₃(x, y, z) = (x ∨ ¬y) ⊕ z + [](uint32_t x, uint32_t y, uint32_t z) noexcept { return (x | ~y) ^ z; }, + + // f₄(x, y, z) = (x ∧ z) ∨ (y ∧ ¬z) ⇔ ((x ⊕ y) ∧ z) ⊕ y + [](uint32_t x, uint32_t y, uint32_t z) noexcept { return ((x ^ y) & z) ^ y; }, + + // f₅(x, y, z) = x ⊕ (y ∨ ¬z) + [](uint32_t x, uint32_t y, uint32_t z) noexcept { return x ^ (y | ~z); }, +}; + +/// Added constants. +constexpr uint32_t constants[L][R] = { + { + 0, + 0x5a827999, + 0x6ed9eba1, + 0x8f1bbcdc, + 0xa953fd4e, + }, + { + 0x50a28be6, + 0x5c4dd124, + 0x6d703ef3, + 0x7a6d76e9, + 0, + }, +}; + + +/// Selection of message word. +constexpr size_t word_index[L][N] = { + { + /*r ( 0..15) = */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // + /*r (16..31) = */ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, // + /*r (32..47) = */ 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, // + /*r (48..63) = */ 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, // + /*r (64..79) = */ 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, // + }, + { + /*r′( 0..15) = */ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, // + /*r′(16..31) = */ 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, // + /*r′(32..47) = */ 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, // + /*r′(48..63) = */ 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, // + /*r′(64..79) = */ 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, // + }, +}; + +/// Amount for rotate left. +constexpr int rotate_amount[L][N] = { + { + /* s ( 0..15) = */ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, // + /* s (16..31) = */ 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, // + /* s (32..47) = */ 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, // + /* s (48..63) = */ 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, // + /* s (64..79) = */ 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, // + }, + { + /* s′( 0..15) = */ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, // + /* s′(16..31) = */ 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, // + /* s′(32..47) = */ 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, // + /* s′(48..63) = */ 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, // + /* s′(64..79) = */ 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, // + }, +}; + +/// Converts native representation to/from little-endian. +inline auto to_le(std::integral auto x) noexcept +{ + if constexpr (std::endian::native == std::endian::big) + return byteswap(x); + return x; +} + +template +inline T load_le(const std::byte* data) noexcept +{ + std::array bytes{}; + std::copy_n(data, sizeof(T), bytes.begin()); + return to_le(std::bit_cast(bytes)); +} + +inline std::byte* store_le(std::byte* out, std::integral auto x) noexcept +{ + return std::ranges::copy(std::bit_cast>(to_le(x)), out).out; +} + +template +inline void step(State z[L], const std::byte* block) noexcept +{ + static constexpr auto I = J / B; // round index + static constexpr BinaryFunction fs[]{binary_functions[I], binary_functions[R - 1 - I]}; + + for (size_t i = 0; i < L; ++i) + { + const auto f = fs[i]; + const auto w = load_le(&block[sizeof(uint32_t) * word_index[i][J]]); + const auto k = constants[i][I]; + const auto s = rotate_amount[i][J]; + + const auto a = z[i][0]; + const auto b = z[i][1]; + const auto c = z[i][2]; + const auto d = z[i][3]; + const auto e = z[i][4]; + + z[i][0] = e; + z[i][1] = std::rotl(a + f(b, c, d) + w + k, s) + e; + z[i][2] = b; + z[i][3] = std::rotl(c, 10); + z[i][4] = d; + } +} + + +// TODO(C++23): This could be a lambda, but [[always_inline]] does not work. +// TODO: Try arguments instead of capture. +template +[[gnu::always_inline]] inline void steps( + State z[L], const std::byte* block, std::integer_sequence) noexcept +{ + (step(z, block), ...); +} + +void compress(State& h, const std::byte* block) noexcept +{ + State z[L]{h, h}; + steps(z, block, std::make_index_sequence{}); + + State t; + for (size_t i = 0, M = t.size(); i < M; ++i) + t[i] = h[(i + 1) % M] + z[0][(i + 2) % M] + z[1][(i + 3) % M]; + h = t; +} +} // namespace + +void ripemd160(std::byte hash[RIPEMD160_HASH_SIZE], const std::byte* data, size_t size) noexcept +{ + static constexpr size_t BLOCK_SIZE = B * sizeof(uint32_t); + State h{0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; + + const auto tail_size = size % BLOCK_SIZE; + for (const auto tail_begin = &data[size - tail_size]; data != tail_begin; data += BLOCK_SIZE) + compress(h, data); + + { + std::array padding_block{}; + const auto padded_tail_end = std::copy_n(data, tail_size, padding_block.data()); + *padded_tail_end = std::byte{0x80}; // The padding bit placed just after the input bytes. + + // Store the input length in bits in the last two words of the padded block. + const auto length_in_bits = uint64_t{size} * 8; + const auto length_begin = &padding_block[BLOCK_SIZE - sizeof(length_in_bits)]; + if (padded_tail_end >= length_begin) // If not enough space, create one more block. + { + compress(h, padding_block.data()); + padding_block = {}; + } + store_le(length_begin, length_in_bits); + compress(h, padding_block.data()); + } + + for (const auto e : h) + hash = store_le(hash, e); +} +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/ripemd160.hpp b/lib/evmone_precompiles/ripemd160.hpp new file mode 100644 index 00000000..111a66dd --- /dev/null +++ b/lib/evmone_precompiles/ripemd160.hpp @@ -0,0 +1,21 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include + +namespace evmone::crypto +{ +/// The size (20 bytes) of the RIPEMD-160 message digest. +static constexpr std::size_t RIPEMD160_HASH_SIZE = 160 / 8; + +/// Computes the RIPEMD-160 hash function. +/// +/// @param[out] hash The result message digest is written to the provided memory. +/// @param data The input data. +/// @param size The size of the input data. +void ripemd160( + std::byte hash[RIPEMD160_HASH_SIZE], const std::byte* data, std::size_t size) noexcept; + +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/secp256k1.cpp b/lib/evmone_precompiles/secp256k1.cpp new file mode 100644 index 00000000..a44deb33 --- /dev/null +++ b/lib/evmone_precompiles/secp256k1.cpp @@ -0,0 +1,776 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#include "secp256k1.hpp" +#include + +namespace evmmax::secp256k1 +{ +namespace +{ +constexpr ModArith Fp{FieldPrime}; +constexpr auto B = Fp.to_mont(7); +constexpr auto B3 = Fp.to_mont(7 * 3); + +constexpr Point G{0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798_u256, + 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8_u256}; +} // namespace + +// FIXME: Change to "uncompress_point". +std::optional calculate_y( + const ModArith& m, const uint256& x, bool y_parity) noexcept +{ + // Calculate sqrt(x^3 + 7) + const auto x3 = m.mul(m.mul(x, x), x); + const auto y = field_sqrt(m, m.add(x3, B)); + if (!y.has_value()) + return std::nullopt; + + // Negate if different parity requested + const auto candidate_parity = (m.from_mont(*y) & 1) != 0; + return (candidate_parity == y_parity) ? *y : m.sub(0, *y); +} + +Point add(const Point& p, const Point& q) noexcept +{ + if (p.is_inf()) + return q; + if (q.is_inf()) + return p; + + const auto pp = ecc::to_proj(Fp, p); + const auto pq = ecc::to_proj(Fp, q); + + // b3 == 21 for y^2 == x^3 + 7 + const auto r = ecc::add(Fp, pp, pq, B3); + return ecc::to_affine(Fp, field_inv, r); +} + +Point mul(const Point& p, const uint256& c) noexcept +{ + if (p.is_inf()) + return p; + + if (c == 0) + return {0, 0}; + + const auto r = ecc::mul(Fp, ecc::to_proj(Fp, p), c, B3); + return ecc::to_affine(Fp, field_inv, r); +} + +evmc::address to_address(const Point& pt) noexcept +{ + // This performs Ethereum's address hashing on an uncompressed pubkey. + uint8_t serialized[64]; + intx::be::unsafe::store(serialized, pt.x); + intx::be::unsafe::store(serialized + 32, pt.y); + + const auto hashed = ethash::keccak256(serialized, sizeof(serialized)); + evmc::address ret{}; + std::memcpy(ret.bytes, hashed.bytes + 12, 20); + + return ret; +} + +std::optional secp256k1_ecdsa_recover( + const ethash::hash256& e, const uint256& r, const uint256& s, bool v) noexcept +{ + // Follows + // https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery + + // 1. Validate r and s are within [1, n-1]. + if (r == 0 || r >= Order || s == 0 || s >= Order) + return std::nullopt; + + // 3. Hash of the message is already calculated in e. + // 4. Convert hash e to z field element by doing z = e % n. + // https://www.rfc-editor.org/rfc/rfc6979#section-2.3.2 + // We can do this by n - e because n > 2^255. + static_assert(Order > 1_u256 << 255); + auto z = intx::be::load(e.bytes); + if (z >= Order) + z -= Order; + + + const ModArith n{Order}; + + // 5. Calculate u1 and u2. + const auto r_n = n.to_mont(r); + const auto r_inv = scalar_inv(n, r_n); + + const auto z_mont = n.to_mont(z); + const auto z_neg = n.sub(0, z_mont); + const auto u1_mont = n.mul(z_neg, r_inv); + const auto u1 = n.from_mont(u1_mont); + + const auto s_mont = n.to_mont(s); + const auto u2_mont = n.mul(s_mont, r_inv); + const auto u2 = n.from_mont(u2_mont); + + // 2. Calculate y coordinate of R from r and v. + const auto r_mont = Fp.to_mont(r); + const auto y_mont = calculate_y(Fp, r_mont, v); + if (!y_mont.has_value()) + return std::nullopt; + const auto y = Fp.from_mont(*y_mont); + + // 6. Calculate public key point Q. + const auto R = ecc::to_proj(Fp, {r, y}); + const auto pG = ecc::to_proj(Fp, G); + const auto T1 = ecc::mul(Fp, pG, u1, B3); + const auto T2 = ecc::mul(Fp, R, u2, B3); + const auto pQ = ecc::add(Fp, T1, T2, B3); + + const auto Q = ecc::to_affine(Fp, field_inv, pQ); + + // Any other validity check needed? + if (Q.is_inf()) + return std::nullopt; + + return Q; +} + +std::optional ecrecover( + const ethash::hash256& e, const uint256& r, const uint256& s, bool v) noexcept +{ + const auto point = secp256k1_ecdsa_recover(e, r, s, v); + if (!point.has_value()) + return std::nullopt; + + return to_address(*point); +} + +uint256 field_inv(const ModArith& m, const uint256& x) noexcept +{ + // Computes modular exponentiation + // x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d + // Operations: 255 squares 15 multiplies + // Generated by github.com/mmcloughlin/addchain v0.4.0. + // addchain search 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d + // > secp256k1_field_inv.acc + // addchain gen -tmpl expmod.tmpl secp256k1_field_inv.acc + // > secp256k1_field_inv.cpp + // + // Exponentiation computation is derived from the addition chain: + // + // _10 = 2*1 + // _100 = 2*_10 + // _101 = 1 + _100 + // _111 = _10 + _101 + // _1110 = 2*_111 + // _111000 = _1110 << 2 + // _111111 = _111 + _111000 + // i13 = _111111 << 4 + _1110 + // x12 = i13 << 2 + _111 + // x22 = x12 << 10 + i13 + 1 + // i29 = 2*x22 + // i31 = i29 << 2 + // i54 = i31 << 22 + i31 + // i122 = (i54 << 20 + i29) << 46 + i54 + // x223 = i122 << 110 + i122 + _111 + // i269 = ((x223 << 23 + x22) << 7 + _101) << 3 + // return _101 + i269 + + // Allocate Temporaries. + uint256 z; + uint256 t0; + uint256 t1; + uint256 t2; + uint256 t3; + uint256 t4; + + // Step 1: t0 = x^0x2 + t0 = m.mul(x, x); + + // Step 2: z = x^0x4 + z = m.mul(t0, t0); + + // Step 3: z = x^0x5 + z = m.mul(x, z); + + // Step 4: t1 = x^0x7 + t1 = m.mul(t0, z); + + // Step 5: t0 = x^0xe + t0 = m.mul(t1, t1); + + // Step 7: t2 = x^0x38 + t2 = m.mul(t0, t0); + for (int i = 1; i < 2; ++i) + t2 = m.mul(t2, t2); + + // Step 8: t2 = x^0x3f + t2 = m.mul(t1, t2); + + // Step 12: t2 = x^0x3f0 + for (int i = 0; i < 4; ++i) + t2 = m.mul(t2, t2); + + // Step 13: t0 = x^0x3fe + t0 = m.mul(t0, t2); + + // Step 15: t2 = x^0xff8 + t2 = m.mul(t0, t0); + for (int i = 1; i < 2; ++i) + t2 = m.mul(t2, t2); + + // Step 16: t2 = x^0xfff + t2 = m.mul(t1, t2); + + // Step 26: t2 = x^0x3ffc00 + for (int i = 0; i < 10; ++i) + t2 = m.mul(t2, t2); + + // Step 27: t0 = x^0x3ffffe + t0 = m.mul(t0, t2); + + // Step 28: t0 = x^0x3fffff + t0 = m.mul(x, t0); + + // Step 29: t3 = x^0x7ffffe + t3 = m.mul(t0, t0); + + // Step 31: t2 = x^0x1fffff8 + t2 = m.mul(t3, t3); + for (int i = 1; i < 2; ++i) + t2 = m.mul(t2, t2); + + // Step 53: t4 = x^0x7ffffe000000 + t4 = m.mul(t2, t2); + for (int i = 1; i < 22; ++i) + t4 = m.mul(t4, t4); + + // Step 54: t2 = x^0x7ffffffffff8 + t2 = m.mul(t2, t4); + + // Step 74: t4 = x^0x7ffffffffff800000 + t4 = m.mul(t2, t2); + for (int i = 1; i < 20; ++i) + t4 = m.mul(t4, t4); + + // Step 75: t3 = x^0x7fffffffffffffffe + t3 = m.mul(t3, t4); + + // Step 121: t3 = x^0x1ffffffffffffffff800000000000 + for (int i = 0; i < 46; ++i) + t3 = m.mul(t3, t3); + + // Step 122: t2 = x^0x1fffffffffffffffffffffffffff8 + t2 = m.mul(t2, t3); + + // Step 232: t3 = x^0x7ffffffffffffffffffffffffffe0000000000000000000000000000 + t3 = m.mul(t2, t2); + for (int i = 1; i < 110; ++i) + t3 = m.mul(t3, t3); + + // Step 233: t2 = x^0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffff8 + t2 = m.mul(t2, t3); + + // Step 234: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff + t1 = m.mul(t1, t2); + + // Step 257: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff800000 + for (int i = 0; i < 23; ++i) + t1 = m.mul(t1, t1); + + // Step 258: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff + t0 = m.mul(t0, t1); + + // Step 265: t0 = x^0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff80 + for (int i = 0; i < 7; ++i) + t0 = m.mul(t0, t0); + + // Step 266: t0 = x^0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff85 + t0 = m.mul(z, t0); + + // Step 269: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc28 + for (int i = 0; i < 3; ++i) + t0 = m.mul(t0, t0); + + // Step 270: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d + z = m.mul(z, t0); + + return z; +} + +std::optional field_sqrt(const ModArith& m, const uint256& x) noexcept +{ + // Computes modular exponentiation + // x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c + // Operations: 253 squares 13 multiplies + // Main part generated by github.com/mmcloughlin/addchain v0.4.0. + // addchain search 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c + // > secp256k1_sqrt.acc + // addchain gen -tmpl expmod.tmpl secp256k1_sqrt.acc + // > secp256k1_sqrt.cpp + // + // Exponentiation computation is derived from the addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _1100 = _11 << 2 + // _1111 = _11 + _1100 + // _11110 = 2*_1111 + // _11111 = 1 + _11110 + // _1111100 = _11111 << 2 + // _1111111 = _11 + _1111100 + // x11 = _1111111 << 4 + _1111 + // x22 = x11 << 11 + x11 + // x27 = x22 << 5 + _11111 + // x54 = x27 << 27 + x27 + // x108 = x54 << 54 + x54 + // x216 = x108 << 108 + x108 + // x223 = x216 << 7 + _1111111 + // return ((x223 << 23 + x22) << 6 + _11) << 2 + + // Allocate Temporaries. + uint256 z; + uint256 t0; + uint256 t1; + uint256 t2; + uint256 t3; + + + // Step 1: z = x^0x2 + z = m.mul(x, x); + + // Step 2: z = x^0x3 + z = m.mul(x, z); + + // Step 4: t0 = x^0xc + t0 = m.mul(z, z); + for (int i = 1; i < 2; ++i) + t0 = m.mul(t0, t0); + + // Step 5: t0 = x^0xf + t0 = m.mul(z, t0); + + // Step 6: t1 = x^0x1e + t1 = m.mul(t0, t0); + + // Step 7: t2 = x^0x1f + t2 = m.mul(x, t1); + + // Step 9: t1 = x^0x7c + t1 = m.mul(t2, t2); + for (int i = 1; i < 2; ++i) + t1 = m.mul(t1, t1); + + // Step 10: t1 = x^0x7f + t1 = m.mul(z, t1); + + // Step 14: t3 = x^0x7f0 + t3 = m.mul(t1, t1); + for (int i = 1; i < 4; ++i) + t3 = m.mul(t3, t3); + + // Step 15: t0 = x^0x7ff + t0 = m.mul(t0, t3); + + // Step 26: t3 = x^0x3ff800 + t3 = m.mul(t0, t0); + for (int i = 1; i < 11; ++i) + t3 = m.mul(t3, t3); + + // Step 27: t0 = x^0x3fffff + t0 = m.mul(t0, t3); + + // Step 32: t3 = x^0x7ffffe0 + t3 = m.mul(t0, t0); + for (int i = 1; i < 5; ++i) + t3 = m.mul(t3, t3); + + // Step 33: t2 = x^0x7ffffff + t2 = m.mul(t2, t3); + + // Step 60: t3 = x^0x3ffffff8000000 + t3 = m.mul(t2, t2); + for (int i = 1; i < 27; ++i) + t3 = m.mul(t3, t3); + + // Step 61: t2 = x^0x3fffffffffffff + t2 = m.mul(t2, t3); + + // Step 115: t3 = x^0xfffffffffffffc0000000000000 + t3 = m.mul(t2, t2); + for (int i = 1; i < 54; ++i) + t3 = m.mul(t3, t3); + + // Step 116: t2 = x^0xfffffffffffffffffffffffffff + t2 = m.mul(t2, t3); + + // Step 224: t3 = x^0xfffffffffffffffffffffffffff000000000000000000000000000 + t3 = m.mul(t2, t2); + for (int i = 1; i < 108; ++i) + t3 = m.mul(t3, t3); + + // Step 225: t2 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffffffff + t2 = m.mul(t2, t3); + + // Step 232: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffff80 + for (int i = 0; i < 7; ++i) + t2 = m.mul(t2, t2); + + // Step 233: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff + t1 = m.mul(t1, t2); + + // Step 256: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff800000 + for (int i = 0; i < 23; ++i) + t1 = m.mul(t1, t1); + + // Step 257: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff + t0 = m.mul(t0, t1); + + // Step 263: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc0 + for (int i = 0; i < 6; ++i) + t0 = m.mul(t0, t0); + + // Step 264: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc3 + z = m.mul(z, t0); + + // Step 266: z = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c + for (int i = 0; i < 2; ++i) + z = m.mul(z, z); + + if (m.mul(z, z) != x) + return std::nullopt; // Computed value is not the square root. + + return z; +} + +uint256 scalar_inv(const ModArith& m, const uint256& x) noexcept +{ + // Computes modular exponentiation + // x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f + // Operations: 253 squares 40 multiplies + // Generated by github.com/mmcloughlin/addchain v0.4.0. + // addchain search 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f + // > secp256k1_scalar_inv.acc + // addchain gen -tmpl expmod.tmpl secp256k1_scalar_inv.acc + // > secp256k1_scalar_inv.cpp + // + // Exponentiation computation is derived from the addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _111 = _10 + _101 + // _1001 = _10 + _111 + // _1011 = _10 + _1001 + // _1101 = _10 + _1011 + // _110100 = _1101 << 2 + // _111111 = _1011 + _110100 + // _1111110 = 2*_111111 + // _1111111 = 1 + _1111110 + // _11111110 = 2*_1111111 + // _11111111 = 1 + _11111110 + // i17 = _11111111 << 3 + // i19 = i17 << 2 + // i20 = 2*i19 + // i21 = 2*i20 + // i39 = (i21 << 7 + i20) << 9 + i21 + // i73 = (i39 << 6 + i19) << 26 + i39 + // x127 = (i73 << 4 + i17) << 60 + i73 + _1111111 + // i154 = ((x127 << 5 + _1011) << 3 + _101) << 4 + // i166 = ((_101 + i154) << 4 + _111) << 5 + _1101 + // i181 = ((i166 << 2 + _11) << 5 + _111) << 6 + // i193 = ((_1101 + i181) << 5 + _1011) << 4 + _1101 + // i214 = ((i193 << 3 + 1) << 6 + _101) << 10 + // i230 = ((_111 + i214) << 4 + _111) << 9 + _11111111 + // i247 = ((i230 << 5 + _1001) << 6 + _1011) << 4 + // i261 = ((_1101 + i247) << 5 + _11) << 6 + _1101 + // i283 = ((i261 << 10 + _1101) << 4 + _1001) << 6 + // return (1 + i283) << 8 + _111111 + + // Allocate Temporaries. + uint256 z; + uint256 t0; + uint256 t1; + uint256 t2; + uint256 t3; + uint256 t4; + uint256 t5; + uint256 t6; + uint256 t7; + uint256 t8; + uint256 t9; + uint256 t10; + uint256 t11; + uint256 t12; + + // Step 1: z = x^0x2 + z = m.mul(x, x); + + // Step 2: t2 = x^0x3 + t2 = m.mul(x, z); + + // Step 3: t6 = x^0x5 + t6 = m.mul(z, t2); + + // Step 4: t5 = x^0x7 + t5 = m.mul(z, t6); + + // Step 5: t0 = x^0x9 + t0 = m.mul(z, t5); + + // Step 6: t3 = x^0xb + t3 = m.mul(z, t0); + + // Step 7: t1 = x^0xd + t1 = m.mul(z, t3); + + // Step 9: z = x^0x34 + z = m.mul(t1, t1); + for (int i = 1; i < 2; ++i) + z = m.mul(z, z); + + // Step 10: z = x^0x3f + z = m.mul(t3, z); + + // Step 11: t4 = x^0x7e + t4 = m.mul(z, z); + + // Step 12: t7 = x^0x7f + t7 = m.mul(x, t4); + + // Step 13: t4 = x^0xfe + t4 = m.mul(t7, t7); + + // Step 14: t4 = x^0xff + t4 = m.mul(x, t4); + + // Step 17: t9 = x^0x7f8 + t9 = m.mul(t4, t4); + for (int i = 1; i < 3; ++i) + t9 = m.mul(t9, t9); + + // Step 19: t10 = x^0x1fe0 + t10 = m.mul(t9, t9); + for (int i = 1; i < 2; ++i) + t10 = m.mul(t10, t10); + + // Step 20: t11 = x^0x3fc0 + t11 = m.mul(t10, t10); + + // Step 21: t8 = x^0x7f80 + t8 = m.mul(t11, t11); + + // Step 28: t12 = x^0x3fc000 + t12 = m.mul(t8, t8); + for (int i = 1; i < 7; ++i) + t12 = m.mul(t12, t12); + + // Step 29: t11 = x^0x3fffc0 + t11 = m.mul(t11, t12); + + // Step 38: t11 = x^0x7fff8000 + for (int i = 0; i < 9; ++i) + t11 = m.mul(t11, t11); + + // Step 39: t8 = x^0x7fffff80 + t8 = m.mul(t8, t11); + + // Step 45: t11 = x^0x1fffffe000 + t11 = m.mul(t8, t8); + for (int i = 1; i < 6; ++i) + t11 = m.mul(t11, t11); + + // Step 46: t10 = x^0x1fffffffe0 + t10 = m.mul(t10, t11); + + // Step 72: t10 = x^0x7fffffff80000000 + for (int i = 0; i < 26; ++i) + t10 = m.mul(t10, t10); + + // Step 73: t8 = x^0x7fffffffffffff80 + t8 = m.mul(t8, t10); + + // Step 77: t10 = x^0x7fffffffffffff800 + t10 = m.mul(t8, t8); + for (int i = 1; i < 4; ++i) + t10 = m.mul(t10, t10); + + // Step 78: t9 = x^0x7fffffffffffffff8 + t9 = m.mul(t9, t10); + + // Step 138: t9 = x^0x7fffffffffffffff8000000000000000 + for (int i = 0; i < 60; ++i) + t9 = m.mul(t9, t9); + + // Step 139: t8 = x^0x7fffffffffffffffffffffffffffff80 + t8 = m.mul(t8, t9); + + // Step 140: t7 = x^0x7fffffffffffffffffffffffffffffff + t7 = m.mul(t7, t8); + + // Step 145: t7 = x^0xfffffffffffffffffffffffffffffffe0 + for (int i = 0; i < 5; ++i) + t7 = m.mul(t7, t7); + + // Step 146: t7 = x^0xfffffffffffffffffffffffffffffffeb + t7 = m.mul(t3, t7); + + // Step 149: t7 = x^0x7fffffffffffffffffffffffffffffff58 + for (int i = 0; i < 3; ++i) + t7 = m.mul(t7, t7); + + // Step 150: t7 = x^0x7fffffffffffffffffffffffffffffff5d + t7 = m.mul(t6, t7); + + // Step 154: t7 = x^0x7fffffffffffffffffffffffffffffff5d0 + for (int i = 0; i < 4; ++i) + t7 = m.mul(t7, t7); + + // Step 155: t7 = x^0x7fffffffffffffffffffffffffffffff5d5 + t7 = m.mul(t6, t7); + + // Step 159: t7 = x^0x7fffffffffffffffffffffffffffffff5d50 + for (int i = 0; i < 4; ++i) + t7 = m.mul(t7, t7); + + // Step 160: t7 = x^0x7fffffffffffffffffffffffffffffff5d57 + t7 = m.mul(t5, t7); + + // Step 165: t7 = x^0xfffffffffffffffffffffffffffffffebaae0 + for (int i = 0; i < 5; ++i) + t7 = m.mul(t7, t7); + + // Step 166: t7 = x^0xfffffffffffffffffffffffffffffffebaaed + t7 = m.mul(t1, t7); + + // Step 168: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb4 + for (int i = 0; i < 2; ++i) + t7 = m.mul(t7, t7); + + // Step 169: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb7 + t7 = m.mul(t2, t7); + + // Step 174: t7 = x^0x7fffffffffffffffffffffffffffffff5d576e0 + for (int i = 0; i < 5; ++i) + t7 = m.mul(t7, t7); + + // Step 175: t7 = x^0x7fffffffffffffffffffffffffffffff5d576e7 + t7 = m.mul(t5, t7); + + // Step 181: t7 = x^0x1fffffffffffffffffffffffffffffffd755db9c0 + for (int i = 0; i < 6; ++i) + t7 = m.mul(t7, t7); + + // Step 182: t7 = x^0x1fffffffffffffffffffffffffffffffd755db9cd + t7 = m.mul(t1, t7); + + // Step 187: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb739a0 + for (int i = 0; i < 5; ++i) + t7 = m.mul(t7, t7); + + // Step 188: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb739ab + t7 = m.mul(t3, t7); + + // Step 192: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb739ab0 + for (int i = 0; i < 4; ++i) + t7 = m.mul(t7, t7); + + // Step 193: t7 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd + t7 = m.mul(t1, t7); + + // Step 196: t7 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e8 + for (int i = 0; i < 3; ++i) + t7 = m.mul(t7, t7); + + // Step 197: t7 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e9 + t7 = m.mul(x, t7); + + // Step 203: t7 = x^0x7fffffffffffffffffffffffffffffff5d576e7357a40 + for (int i = 0; i < 6; ++i) + t7 = m.mul(t7, t7); + + // Step 204: t6 = x^0x7fffffffffffffffffffffffffffffff5d576e7357a45 + t6 = m.mul(t6, t7); + + // Step 214: t6 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e91400 + for (int i = 0; i < 10; ++i) + t6 = m.mul(t6, t6); + + // Step 215: t6 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e91407 + t6 = m.mul(t5, t6); + + // Step 219: t6 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e914070 + for (int i = 0; i < 4; ++i) + t6 = m.mul(t6, t6); + + // Step 220: t5 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e914077 + t5 = m.mul(t5, t6); + + // Step 229: t5 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280ee00 + for (int i = 0; i < 9; ++i) + t5 = m.mul(t5, t5); + + // Step 230: t4 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff + t4 = m.mul(t4, t5); + + // Step 235: t4 = x^0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe0 + for (int i = 0; i < 5; ++i) + t4 = m.mul(t4, t4); + + // Step 236: t4 = x^0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe9 + t4 = m.mul(t0, t4); + + // Step 242: t4 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e9140777fa40 + for (int i = 0; i < 6; ++i) + t4 = m.mul(t4, t4); + + // Step 243: t3 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4b + t3 = m.mul(t3, t4); + + // Step 247: t3 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4b0 + for (int i = 0; i < 4; ++i) + t3 = m.mul(t3, t3); + + // Step 248: t3 = x^0x1fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4bd + t3 = m.mul(t1, t3); + + // Step 253: t3 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a0 + for (int i = 0; i < 5; ++i) + t3 = m.mul(t3, t3); + + // Step 254: t2 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3 + t2 = m.mul(t2, t3); + + // Step 260: t2 = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8c0 + for (int i = 0; i < 6; ++i) + t2 = m.mul(t2, t2); + + // Step 261: t2 = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd + t2 = m.mul(t1, t2); + + // Step 271: t2 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a33400 + for (int i = 0; i < 10; ++i) + t2 = m.mul(t2, t2); + + // Step 272: t1 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3340d + t1 = m.mul(t1, t2); + + // Step 276: t1 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3340d0 + for (int i = 0; i < 4; ++i) + t1 = m.mul(t1, t1); + + // Step 277: t0 = x^0x3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3340d9 + t0 = m.mul(t0, t1); + + // Step 283: t0 = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03640 + for (int i = 0; i < 6; ++i) + t0 = m.mul(t0, t0); + + // Step 284: t0 = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641 + t0 = m.mul(x, t0); + + // Step 292: t0 = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364100 + for (int i = 0; i < 8; ++i) + t0 = m.mul(t0, t0); + + // Step 293: z = x^0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f + z = m.mul(z, t0); + + return z; +} +} // namespace evmmax::secp256k1 diff --git a/lib/evmone_precompiles/secp256k1.hpp b/lib/evmone_precompiles/secp256k1.hpp new file mode 100644 index 00000000..da9e0a33 --- /dev/null +++ b/lib/evmone_precompiles/secp256k1.hpp @@ -0,0 +1,69 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "ecc.hpp" +#include +#include +#include + +namespace evmmax::secp256k1 +{ +using namespace intx; + +/// The secp256k1 field prime number (P). +inline constexpr auto FieldPrime = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_u256; + +/// The secp256k1 curve group order (N). +inline constexpr auto Order = + 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_u256; + +using Point = ecc::Point; + + +/// Modular inversion for secp256k1 prime field. +/// +/// Computes 1/x mod P modular inversion by computing modular exponentiation x^(P-2), +/// where P is ::FieldPrime. +uint256 field_inv(const ModArith& m, const uint256& x) noexcept; + +/// Square root for secp256k1 prime field. +/// +/// Computes √x mod P by computing modular exponentiation x^((P+1)/4), +/// where P is ::FieldPrime. +/// +/// @return Square root of x if it exists, std::nullopt otherwise. +std::optional field_sqrt(const ModArith& m, const uint256& x) noexcept; + +/// Inversion modulo order of secp256k1. +/// +/// Computes 1/x mod N modular inversion by computing modular exponentiation x^(N-2), +/// where N is ::Order. +uint256 scalar_inv(const ModArith& m, const uint256& x) noexcept; + +/// Calculate y coordinate of a point having x coordinate and y parity. +std::optional calculate_y( + const ModArith& m, const uint256& x, bool y_parity) noexcept; + +/// Addition in secp256k1. +/// +/// Computes P ⊕ Q for two points in affine coordinates on the secp256k1 curve, +Point add(const Point& p, const Point& q) noexcept; + +/// Scalar multiplication in secp256k1. +/// +/// Computes [c]P for a point in affine coordinate on the secp256k1 curve, +Point mul(const Point& p, const uint256& c) noexcept; + +/// Convert the secp256k1 point (uncompressed public key) to Ethereum address. +evmc::address to_address(const Point& pt) noexcept; + +std::optional secp256k1_ecdsa_recover( + const ethash::hash256& e, const uint256& r, const uint256& s, bool v) noexcept; + +std::optional ecrecover( + const ethash::hash256& e, const uint256& r, const uint256& s, bool v) noexcept; + +} // namespace evmmax::secp256k1 diff --git a/lib/evmone_precompiles/sha256.cpp b/lib/evmone_precompiles/sha256.cpp new file mode 100644 index 00000000..0dc1b6b2 --- /dev/null +++ b/lib/evmone_precompiles/sha256.cpp @@ -0,0 +1,749 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The Silkworm & evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +/// @file +/// SHA256 implementation. +/// Initial version copied from the Silkworm project (https://github.com/erigontech/silkworm). +/// Based on several bits of code released to public domain: +/// https://github.com/amosnier/sha-2 (Author: Alain Mosnier) +/// https://github.com/noloader/SHA-Intrinsics (Author: Jeffrey Walton) +/// https://github.com/Mysticial/FeatureDetector (Author: Alexander Yee) + +#include "sha256.hpp" +#include +#include +#include + +#if defined(__x86_64__) + +#include +#include + +#elif defined(__aarch64__) && defined(__APPLE__) + +#include + +#if defined(__linux__) +#include +#include +#elif defined(__APPLE__) +#include +#endif // defined(__linux__), defined(__APPLE__) + +#endif // defined(__x86_64__), defined(__aarch64__) + +namespace evmone::crypto +{ + +#define CHUNK_SIZE 64 +#define TOTAL_LEN_LEN 8 + +/* + * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here. + * When useful for clarification, portions of the pseudo-code are reproduced here too. + */ + +/* + * Initialize array of round constants: + * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): + */ +static const uint32_t k[] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, + 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, + 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, + 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, + 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, + 0xbef9a3f7, 0xc67178f2}; + +struct BufferState +{ + const std::byte* p; + size_t len; + size_t total_len; + bool single_one_delivered = false; + bool total_len_delivered = false; + + constexpr BufferState(const std::byte* input, size_t size) + : p{input}, len{size}, total_len{size} + {} +}; + +static bool calc_chunk(uint8_t chunk[CHUNK_SIZE], struct BufferState* state) +{ + if (state->total_len_delivered) + { + return false; + } + + if (state->len >= CHUNK_SIZE) + { + memcpy(chunk, state->p, CHUNK_SIZE); + state->p += CHUNK_SIZE; + state->len -= CHUNK_SIZE; + return true; + } + + size_t space_in_chunk = CHUNK_SIZE - state->len; + if (state->len != 0) + { // avoid adding 0 to nullptr + memcpy(chunk, state->p, state->len); + chunk += state->len; + state->p += state->len; + } + state->len = 0; + + /* If we are here, space_in_chunk is one at minimum. */ + if (!state->single_one_delivered) + { + *chunk++ = 0x80; + space_in_chunk -= 1; + state->single_one_delivered = true; + } + + /* + * Now: + * - either there is enough space left for the total length, and we can conclude, + * - or there is too little space left, and we have to pad the rest of this chunk with zeroes. + * In the latter case, we will conclude at the next invocation of this function. + */ + if (space_in_chunk >= TOTAL_LEN_LEN) + { + const size_t left = space_in_chunk - TOTAL_LEN_LEN; + size_t len = state->total_len; + int i = 0; + memset(chunk, 0x00, left); + chunk += left; + + /* Storing of len * 8 as a big endian 64-bit without overflow. */ + chunk[7] = (uint8_t)(len << 3); + len >>= 5; + for (i = 6; i >= 0; i--) + { + chunk[i] = (uint8_t)len; + len >>= 8; + } + state->total_len_delivered = true; + } + else + { + memset(chunk, 0x00, space_in_chunk); + } + + return true; +} + +[[gnu::always_inline, msvc::forceinline]] inline void sha_256_implementation( + uint32_t h[8], const std::byte* input, size_t len) +{ + /* + * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated + * modulo 2^32. + * + * Note 2: For each round, there is one round constant k[i] and one entry in the message + * schedule array w[i], 0 = i = 63 + * + * Note 3: The compression function uses 8 working variables, a through h + * + * Note 4: Big-endian convention is used when expressing the constants in this pseudocode, + * and when parsing message block data from bytes to words, for example, + * the first word of the input message "abc" after padding is 0x61626380 + */ + + BufferState state{input, len}; + + /* 512-bit chunks is what we will operate on. */ + uint8_t chunk[CHUNK_SIZE]; + + while (calc_chunk(chunk, &state)) + { + unsigned i = 0; + unsigned j = 0; + + uint32_t ah[8]; + /* Initialize working variables to current hash value: */ + for (i = 0; i < 8; i++) + { + ah[i] = h[i]; + } + + const uint8_t* p = chunk; + + /* + * The w-array is really w[64], but since we only need 16 of them at a time, we save stack + * by calculating 16 at a time. + * + * This optimization was not there initially and the rest of the comments about w[64] are + * kept in their initial state. + */ + + /* + * create a 64-entry message schedule array w[0..63] of 32-bit words (The initial values in + * w[0..63] don't matter, so many implementations zero them here) copy chunk into first 16 + * words w[0..15] of the message schedule array + */ + uint32_t w[16]; + + /* Compression function main loop: */ + for (i = 0; i < 4; i++) + { + for (j = 0; j < 16; j++) + { + if (i == 0) + { + w[j] = (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | + (uint32_t)p[3]; + p += 4; + } + else + { + /* Extend the first 16 words into the remaining 48 words w[16..63] of the + * message schedule array: */ + const uint32_t s0 = std::rotr(w[(j + 1) & 0xf], 7) ^ + std::rotr(w[(j + 1) & 0xf], 18) ^ (w[(j + 1) & 0xf] >> 3); + const uint32_t s1 = std::rotr(w[(j + 14) & 0xf], 17) ^ + std::rotr(w[(j + 14) & 0xf], 19) ^ + (w[(j + 14) & 0xf] >> 10); + w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1; + } + const uint32_t s1 = + std::rotr(ah[4], 6) ^ std::rotr(ah[4], 11) ^ std::rotr(ah[4], 25); + const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]); + const uint32_t temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j]; + const uint32_t s0 = + std::rotr(ah[0], 2) ^ std::rotr(ah[0], 13) ^ std::rotr(ah[0], 22); + const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]); + const uint32_t temp2 = s0 + maj; + + ah[7] = ah[6]; + ah[6] = ah[5]; + ah[5] = ah[4]; + ah[4] = ah[3] + temp1; + ah[3] = ah[2]; + ah[2] = ah[1]; + ah[1] = ah[0]; + ah[0] = temp1 + temp2; + } + } + + /* Add the compressed chunk to the current hash value: */ + for (i = 0; i < 8; i++) + { + h[i] += ah[i]; + } + } +} + +static void sha_256_generic(uint32_t h[8], const std::byte* input, size_t len) +{ + sha_256_implementation(h, input, len); +} + +static void (*sha_256_best)(uint32_t h[8], const std::byte* input, size_t len) = sha_256_generic; + +#if defined(__x86_64__) + +__attribute__((target("bmi,bmi2"))) static void sha_256_x86_bmi( + uint32_t h[8], const std::byte* input, size_t len) +{ + sha_256_implementation(h, input, len); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" + +// The following function was adapted from +// https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c +/* Intel SHA extensions using C intrinsics */ +/* Written and place in public domain by Jeffrey Walton */ +/* Based on code from Intel, and by Sean Gulley for */ +/* the miTLS project. */ +__attribute__((target("sha,sse4.1"))) static void sha_256_x86_sha( + uint32_t h[8], const std::byte* input, size_t len) +{ + // NOLINTBEGIN(readability-isolate-declaration) + __m128i STATE0, STATE1; + __m128i MSG, TMP; + __m128i MSG0, MSG1, MSG2, MSG3; + __m128i ABEF_SAVE, CDGH_SAVE; + // NOLINTEND(readability-isolate-declaration) + + const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); + + // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast) + // NOLINTBEGIN(portability-simd-intrinsics) + /* Load initial values */ + TMP = _mm_loadu_si128((const __m128i*)&h[0]); + STATE1 = _mm_loadu_si128((const __m128i*)&h[4]); + + TMP = _mm_shuffle_epi32(TMP, 0xB1); /* CDAB */ + STATE1 = _mm_shuffle_epi32(STATE1, 0x1B); /* EFGH */ + STATE0 = _mm_alignr_epi8(TMP, STATE1, 8); /* ABEF */ + STATE1 = _mm_blend_epi16(STATE1, TMP, 0xF0); /* CDGH */ + + BufferState state{input, len}; + + /* 512-bit chunks is what we will operate on. */ + uint8_t chunk[CHUNK_SIZE]; + + while (calc_chunk(chunk, &state)) + { + /* Save current state */ + ABEF_SAVE = STATE0; + CDGH_SAVE = STATE1; + + /* Rounds 0-3 */ + MSG = _mm_loadu_si128((const __m128i*)(chunk + 0)); + MSG0 = _mm_shuffle_epi8(MSG, MASK); + MSG = _mm_add_epi32( + MSG0, _mm_set_epi64x(0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + + /* Rounds 4-7 */ + MSG1 = _mm_loadu_si128((const __m128i*)(chunk + 16)); + MSG1 = _mm_shuffle_epi8(MSG1, MASK); + MSG = _mm_add_epi32( + MSG1, _mm_set_epi64x(0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); + + /* Rounds 8-11 */ + MSG2 = _mm_loadu_si128((const __m128i*)(chunk + 32)); + MSG2 = _mm_shuffle_epi8(MSG2, MASK); + MSG = _mm_add_epi32( + MSG2, _mm_set_epi64x(0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); + + /* Rounds 12-15 */ + MSG3 = _mm_loadu_si128((const __m128i*)(chunk + 48)); + MSG3 = _mm_shuffle_epi8(MSG3, MASK); + MSG = _mm_add_epi32( + MSG3, _mm_set_epi64x(0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG3, MSG2, 4); + MSG0 = _mm_add_epi32(MSG0, TMP); + MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); + + /* Rounds 16-19 */ + MSG = _mm_add_epi32( + MSG0, _mm_set_epi64x(0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG0, MSG3, 4); + MSG1 = _mm_add_epi32(MSG1, TMP); + MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); + + /* Rounds 20-23 */ + MSG = _mm_add_epi32( + MSG1, _mm_set_epi64x(0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG1, MSG0, 4); + MSG2 = _mm_add_epi32(MSG2, TMP); + MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); + + /* Rounds 24-27 */ + MSG = _mm_add_epi32( + MSG2, _mm_set_epi64x(0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG2, MSG1, 4); + MSG3 = _mm_add_epi32(MSG3, TMP); + MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); + + /* Rounds 28-31 */ + MSG = _mm_add_epi32( + MSG3, _mm_set_epi64x(0x1429296706CA6351ULL, 0xD5A79147C6E00BF3ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG3, MSG2, 4); + MSG0 = _mm_add_epi32(MSG0, TMP); + MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); + + /* Rounds 32-35 */ + MSG = _mm_add_epi32( + MSG0, _mm_set_epi64x(0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG0, MSG3, 4); + MSG1 = _mm_add_epi32(MSG1, TMP); + MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); + + /* Rounds 36-39 */ + MSG = _mm_add_epi32( + MSG1, _mm_set_epi64x(0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG1, MSG0, 4); + MSG2 = _mm_add_epi32(MSG2, TMP); + MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); + + /* Rounds 40-43 */ + MSG = _mm_add_epi32( + MSG2, _mm_set_epi64x(0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG2, MSG1, 4); + MSG3 = _mm_add_epi32(MSG3, TMP); + MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); + + /* Rounds 44-47 */ + MSG = _mm_add_epi32( + MSG3, _mm_set_epi64x(0x106AA070F40E3585ULL, 0xD6990624D192E819ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG3, MSG2, 4); + MSG0 = _mm_add_epi32(MSG0, TMP); + MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); + + /* Rounds 48-51 */ + MSG = _mm_add_epi32( + MSG0, _mm_set_epi64x(0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG0, MSG3, 4); + MSG1 = _mm_add_epi32(MSG1, TMP); + MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); + + /* Rounds 52-55 */ + MSG = _mm_add_epi32( + MSG1, _mm_set_epi64x(0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG1, MSG0, 4); + MSG2 = _mm_add_epi32(MSG2, TMP); + MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + + /* Rounds 56-59 */ + MSG = _mm_add_epi32( + MSG2, _mm_set_epi64x(0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + TMP = _mm_alignr_epi8(MSG2, MSG1, 4); + MSG3 = _mm_add_epi32(MSG3, TMP); + MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + + /* Rounds 60-63 */ + MSG = _mm_add_epi32( + MSG3, _mm_set_epi64x(0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL)); // NOLINT + STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); + MSG = _mm_shuffle_epi32(MSG, 0x0E); + STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); + + /* Combine state */ + STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE); + STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE); + } + + TMP = _mm_shuffle_epi32(STATE0, 0x1B); /* FEBA */ + STATE1 = _mm_shuffle_epi32(STATE1, 0xB1); /* DCHG */ + STATE0 = _mm_blend_epi16(TMP, STATE1, 0xF0); /* DCBA */ + STATE1 = _mm_alignr_epi8(STATE1, TMP, 8); /* ABEF */ + + /* Save state */ + _mm_storeu_si128((__m128i*)&h[0], STATE0); + _mm_storeu_si128((__m128i*)&h[4], STATE1); + // NOLINTEND(portability-simd-intrinsics) + // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast) +} + +#pragma GCC diagnostic pop + +// https://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set +static void cpuid(int info[4], int InfoType) // NOLINT(readability-non-const-parameter) +{ + __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); +} + +__attribute__((constructor)) static void select_sha256_implementation() +{ + int info[4]; + cpuid(info, 0); + const int nIds = info[0]; + + bool hw_sse41 = false; + bool hw_bmi1 = false; + bool hw_bmi2 = false; + bool hw_sha = false; + + if (nIds >= 0x00000001) + { + cpuid(info, 0x00000001); + hw_sse41 = (info[2] & (1 << 19)) != 0; + } + if (nIds >= 0x00000007) + { + cpuid(info, 0x00000007); + hw_bmi1 = (info[1] & (1 << 3)) != 0; + hw_bmi2 = (info[1] & (1 << 8)) != 0; + hw_sha = (info[1] & (1 << 29)) != 0; + } + + if (hw_sse41 && hw_sha) + { + sha_256_best = sha_256_x86_sha; + } + else if (hw_bmi1 && hw_bmi2) + { + sha_256_best = sha_256_x86_bmi; + } +} + +#elif defined(__aarch64__) && defined(__APPLE__) + +// The following function was adapted from +// https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-arm.c +/* sha256-arm.c - ARMv8 SHA extensions using C intrinsics */ +/* Written and placed in public domain by Jeffrey Walton */ +/* Based on code from ARM, and by Johannes Schneiders, Skip */ +/* Hovsmith and Barry O'Rourke for the mbedTLS project. */ +static void sha_256_arm_v8(uint32_t h[8], const std::byte* input, size_t len) +{ + uint32x4_t STATE0, STATE1, ABEF_SAVE, CDGH_SAVE; + uint32x4_t MSG0, MSG1, MSG2, MSG3; + uint32x4_t TMP0, TMP1, TMP2; + + /* Load state */ + STATE0 = vld1q_u32(&h[0]); + STATE1 = vld1q_u32(&h[4]); + + BufferState state{input, len}; + + /* 512-bit chunks is what we will operate on. */ + uint8_t chunk[CHUNK_SIZE]; + + while (calc_chunk(chunk, &state)) + { + /* Save state */ + ABEF_SAVE = STATE0; + CDGH_SAVE = STATE1; + + /* Load message */ + MSG0 = vld1q_u32((const uint32_t*)(chunk + 0)); + MSG1 = vld1q_u32((const uint32_t*)(chunk + 16)); + MSG2 = vld1q_u32((const uint32_t*)(chunk + 32)); + MSG3 = vld1q_u32((const uint32_t*)(chunk + 48)); + + /* Reverse for little endian */ + MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0))); + MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1))); + MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2))); + MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3))); + + TMP0 = vaddq_u32(MSG0, vld1q_u32(&k[0x00])); + + /* Rounds 0-3 */ + MSG0 = vsha256su0q_u32(MSG0, MSG1); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG1, vld1q_u32(&k[0x04])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); + + /* Rounds 4-7 */ + MSG1 = vsha256su0q_u32(MSG1, MSG2); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG2, vld1q_u32(&k[0x08])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); + + /* Rounds 8-11 */ + MSG2 = vsha256su0q_u32(MSG2, MSG3); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG3, vld1q_u32(&k[0x0c])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); + + /* Rounds 12-15 */ + MSG3 = vsha256su0q_u32(MSG3, MSG0); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG0, vld1q_u32(&k[0x10])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); + + /* Rounds 16-19 */ + MSG0 = vsha256su0q_u32(MSG0, MSG1); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG1, vld1q_u32(&k[0x14])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); + + /* Rounds 20-23 */ + MSG1 = vsha256su0q_u32(MSG1, MSG2); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG2, vld1q_u32(&k[0x18])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); + + /* Rounds 24-27 */ + MSG2 = vsha256su0q_u32(MSG2, MSG3); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG3, vld1q_u32(&k[0x1c])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); + + /* Rounds 28-31 */ + MSG3 = vsha256su0q_u32(MSG3, MSG0); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG0, vld1q_u32(&k[0x20])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); + + /* Rounds 32-35 */ + MSG0 = vsha256su0q_u32(MSG0, MSG1); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG1, vld1q_u32(&k[0x24])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); + + /* Rounds 36-39 */ + MSG1 = vsha256su0q_u32(MSG1, MSG2); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG2, vld1q_u32(&k[0x28])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); + + /* Rounds 40-43 */ + MSG2 = vsha256su0q_u32(MSG2, MSG3); + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG3, vld1q_u32(&k[0x2c])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); + + /* Rounds 44-47 */ + MSG3 = vsha256su0q_u32(MSG3, MSG0); + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG0, vld1q_u32(&k[0x30])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); + + /* Rounds 48-51 */ + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG1, vld1q_u32(&k[0x34])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + + /* Rounds 52-55 */ + TMP2 = STATE0; + TMP0 = vaddq_u32(MSG2, vld1q_u32(&k[0x38])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + + /* Rounds 56-59 */ + TMP2 = STATE0; + TMP1 = vaddq_u32(MSG3, vld1q_u32(&k[0x3c])); + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); + + /* Rounds 60-63 */ + TMP2 = STATE0; + STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); + STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); + + /* Combine state */ + STATE0 = vaddq_u32(STATE0, ABEF_SAVE); + STATE1 = vaddq_u32(STATE1, CDGH_SAVE); + } + + /* Save state */ + vst1q_u32(&h[0], STATE0); + vst1q_u32(&h[4], STATE1); +} + +__attribute__((constructor)) static void select_sha256_implementation(void) +{ +#if defined(__linux__) + if ((getauxval(AT_HWCAP) & HWCAP_SHA2) != 0) + { + sha_256_best = sha_256_arm_v8; + } +#elif defined(__APPLE__) + int64_t hw_cap = 0; + size_t size = sizeof(hw_cap); + + if (sysctlbyname("hw.optional.armv8_2_sha3", &hw_cap, &size, NULL, 0) == 0) + { + // Use SHA3 as proxy for SHA2 (sysctl hw doesn't list SHA2 for Apple M1) + if (hw_cap == 1) + { + sha_256_best = sha_256_arm_v8; + } + } +#endif // defined(__linux__), defined(__APPLE__) +} + +#endif // defined(__x86_64__), defined(__aarch64__) + +/* + * Limitations: + * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem + * for large data sizes. + * - SHA algorithms theoretically operate on bit strings. However, this implementation has no + * support for bit string lengths that are not multiples of eight, and it really operates on arrays + * of bytes. In particular, the len parameter is a number of bytes. + */ +void sha256(std::byte hash[SHA256_HASH_SIZE], const std::byte* data, size_t size) +{ + /* + * Initialize hash values: + * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): + */ + uint32_t h[] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19}; + + sha_256_best(h, data, size); + + /* Produce the final hash value (big-endian): */ + for (unsigned i = 0, j = 0; i < 8; i++) + { + hash[j++] = static_cast(h[i] >> 24); + hash[j++] = static_cast(h[i] >> 16); + hash[j++] = static_cast(h[i] >> 8); + hash[j++] = static_cast(h[i]); + } +} + +} // namespace evmone::crypto diff --git a/lib/evmone_precompiles/sha256.hpp b/lib/evmone_precompiles/sha256.hpp new file mode 100644 index 00000000..487191f9 --- /dev/null +++ b/lib/evmone_precompiles/sha256.hpp @@ -0,0 +1,19 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The Silkworm & evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::crypto +{ +/// The size (32 bytes) of the SHA256 message digest. +static constexpr std::size_t SHA256_HASH_SIZE = 256 / 8; + +/// Computes the SHA256 hash function. +/// +/// @param[out] hash The result message digest is written to the provided memory. +/// @param data The input data. +/// @param size The size of the input data. +void sha256(std::byte hash[SHA256_HASH_SIZE], const std::byte* data, size_t size); +} // namespace evmone::crypto diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ddfb58b0..cfc2dd0f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,17 +13,23 @@ find_package(GTest CONFIG REQUIRED) hunter_add_package(benchmark) find_package(benchmark CONFIG REQUIRED) +hunter_add_package(nlohmann_json) +find_package(nlohmann_json CONFIG REQUIRED) + add_subdirectory(utils) add_subdirectory(bench) +add_subdirectory(blockchaintest) add_subdirectory(eofparse) add_subdirectory(integration) add_subdirectory(internal_benchmarks) +add_subdirectory(precompiles_bench) add_subdirectory(state) add_subdirectory(statetest) +add_subdirectory(eoftest) add_subdirectory(t8n) add_subdirectory(unittests) -set(targets evmone-bench evmone-bench-internal evmone-eofparse evmone-state evmone-statetest evmone-t8n evmone-unittests) +set(targets evmone-bench evmone-bench-internal evmone-eofparse evmone-blockchaintest evmone-precompiles-bench evmone-state evmone-statetest evmone-eoftest evmone-t8n evmone-unittests) if(EVMONE_FUZZING) add_subdirectory(eofparsefuzz) diff --git a/test/bench/bench.cpp b/test/bench/bench.cpp index 165e1186..1d6aabd2 100644 --- a/test/bench/bench.cpp +++ b/test/bench/bench.cpp @@ -58,7 +58,7 @@ std::vector load_inputs(const StateTransitionTest& state_t BenchmarkCase load_benchmark(const fs::path& path, const std::string& name_prefix) { std::ifstream f{path}; - auto state_test = evmone::test::load_state_test(f); + auto state_test = std::move(evmone::test::load_state_tests(f).at(0)); const auto name = name_prefix + path.stem().string(); const auto code = state_test.pre_state.get(state_test.multi_tx.to.value()).code; @@ -117,7 +117,7 @@ void register_benchmarks(std::span benchmark_cases) { if (advanced_vm != nullptr) { - RegisterBenchmark(("advanced/analyse/" + b.name).c_str(), [&b](State& state) { + RegisterBenchmark("advanced/analyse/" + b.name, [&b](State& state) { bench_analyse( state, default_revision, b.code); })->Unit(kMicrosecond); @@ -125,7 +125,7 @@ void register_benchmarks(std::span benchmark_cases) if (baseline_vm != nullptr) { - RegisterBenchmark(("baseline/analyse/" + b.name).c_str(), [&b](State& state) { + RegisterBenchmark("baseline/analyse/" + b.name, [&b](State& state) { bench_analyse( state, default_revision, b.code); })->Unit(kMicrosecond); @@ -138,7 +138,7 @@ void register_benchmarks(std::span benchmark_cases) if (advanced_vm != nullptr) { const auto name = "advanced/execute/" + case_name; - RegisterBenchmark(name.c_str(), [&vm = *advanced_vm, &b, &input](State& state) { + RegisterBenchmark(name, [&vm = *advanced_vm, &b, &input](State& state) { bench_advanced_execute(state, vm, b.code, input.input, input.expected_output); })->Unit(kMicrosecond); } @@ -146,7 +146,7 @@ void register_benchmarks(std::span benchmark_cases) if (baseline_vm != nullptr) { const auto name = "baseline/execute/" + case_name; - RegisterBenchmark(name.c_str(), [&vm = *baseline_vm, &b, &input](State& state) { + RegisterBenchmark(name, [&vm = *baseline_vm, &b, &input](State& state) { bench_baseline_execute(state, vm, b.code, input.input, input.expected_output); })->Unit(kMicrosecond); } @@ -154,7 +154,7 @@ void register_benchmarks(std::span benchmark_cases) if (basel_cg_vm != nullptr) { const auto name = "bnocgoto/execute/" + case_name; - RegisterBenchmark(name.c_str(), [&vm = *basel_cg_vm, &b, &input](State& state) { + RegisterBenchmark(name, [&vm = *basel_cg_vm, &b, &input](State& state) { bench_baseline_execute(state, vm, b.code, input.input, input.expected_output); })->Unit(kMicrosecond); } @@ -162,7 +162,7 @@ void register_benchmarks(std::span benchmark_cases) for (auto& [vm_name, vm] : registered_vms) { const auto name = std::string{vm_name} + "/total/" + case_name; - RegisterBenchmark(name.c_str(), [&vm_ = vm, &b, &input](State& state) { + RegisterBenchmark(name, [&vm_ = vm, &b, &input](State& state) { bench_evmc_execute(state, vm_, b.code, input.input, input.expected_output); })->Unit(kMicrosecond); } diff --git a/test/bench/helpers.hpp b/test/bench/helpers.hpp index 19605a31..f19fb1ad 100644 --- a/test/bench/helpers.hpp +++ b/test/bench/helpers.hpp @@ -40,9 +40,9 @@ inline advanced::AdvancedCodeAnalysis advanced_analyse(evmc_revision rev, bytes_ return advanced::analyze(rev, code); } -inline baseline::CodeAnalysis baseline_analyse(evmc_revision rev, bytes_view code) +inline baseline::CodeAnalysis baseline_analyse(evmc_revision /*rev*/, bytes_view code) { - return baseline::analyze(rev, code); + return baseline::analyze(code, true); // Always enable EOF. } inline FakeCodeAnalysis evmc_analyse(evmc_revision /*rev*/, bytes_view /*code*/) @@ -59,13 +59,15 @@ inline evmc::Result advanced_execute(evmc::VM& /*vm*/, advanced::AdvancedExecuti return evmc::Result{execute(exec_state, analysis)}; } -inline evmc::Result baseline_execute(evmc::VM& c_vm, ExecutionState& exec_state, +inline evmc::Result baseline_execute(evmc::VM& c_vm, [[maybe_unused]] ExecutionState& exec_state, const baseline::CodeAnalysis& analysis, const evmc_message& msg, evmc_revision rev, - evmc::Host& host, bytes_view code) + evmc::Host& host, [[maybe_unused]] bytes_view code) { - const auto& vm = *static_cast(c_vm.get_raw_pointer()); - exec_state.reset(msg, rev, host.get_interface(), host.to_context(), code, {}, 0); - return evmc::Result{baseline::execute(vm, msg.gas, exec_state, analysis)}; + (void)rev; + (void)host; + auto& vm = *static_cast(c_vm.get_raw_pointer()); + return evmc::Result{ + baseline::execute(vm, msg, analysis, exec_state)}; } inline evmc::Result evmc_execute(evmc::VM& vm, FakeExecutionState& /*exec_state*/, diff --git a/test/bench/synthetic_benchmarks.cpp b/test/bench/synthetic_benchmarks.cpp index 75605409..b4184df9 100644 --- a/test/bench/synthetic_benchmarks.cpp +++ b/test/bench/synthetic_benchmarks.cpp @@ -256,9 +256,10 @@ void register_synthetic_benchmarks() for (auto& [vm_name, vm] : registered_vms) { - RegisterBenchmark((std::string{vm_name} + "/total/synth/loop_v1").c_str(), + // TODO(clang16): Simplify lambda capture [&vm]. + RegisterBenchmark(std::string{vm_name} + "/total/synth/loop_v1", [&vm_ = vm](State& state) { bench_evmc_execute(state, vm_, generate_loop_v1({})); }); - RegisterBenchmark((std::string{vm_name} + "/total/synth/loop_v2").c_str(), + RegisterBenchmark(std::string{vm_name} + "/total/synth/loop_v2", [&vm_ = vm](State& state) { bench_evmc_execute(state, vm_, generate_loop_v2({})); }); } @@ -266,7 +267,7 @@ void register_synthetic_benchmarks() { for (auto& [vm_name, vm] : registered_vms) { - RegisterBenchmark((std::string{vm_name} + "/total/synth/" + to_string(params)).c_str(), + RegisterBenchmark(std::string{vm_name} + "/total/synth/" + to_string(params), [&vm_ = vm, params]( State& state) { bench_evmc_execute(state, vm_, generate_code(params)); }) ->Unit(kMicrosecond); diff --git a/test/blockchaintest/.clang-tidy b/test/blockchaintest/.clang-tidy new file mode 100644 index 00000000..efc628c8 --- /dev/null +++ b/test/blockchaintest/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: > + -clang-analyzer-cplusplus.NewDeleteLeaks diff --git a/test/blockchaintest/CMakeLists.txt b/test/blockchaintest/CMakeLists.txt new file mode 100644 index 00000000..6823b4c6 --- /dev/null +++ b/test/blockchaintest/CMakeLists.txt @@ -0,0 +1,13 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +add_executable(evmone-blockchaintest) +target_link_libraries(evmone-blockchaintest PRIVATE evmone evmone::statetestutils evmone-buildinfo GTest::gtest) +target_include_directories(evmone-blockchaintest PRIVATE ${evmone_private_include_dir}) +target_sources( + evmone-blockchaintest PRIVATE + blockchaintest.hpp + blockchaintest.cpp + blockchaintest_runner.cpp +) diff --git a/test/blockchaintest/blockchaintest.cpp b/test/blockchaintest/blockchaintest.cpp new file mode 100644 index 00000000..e34fe3d6 --- /dev/null +++ b/test/blockchaintest/blockchaintest.cpp @@ -0,0 +1,105 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "blockchaintest.hpp" +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace +{ +class BlockchainGTest : public testing::Test +{ + fs::path m_json_test_file; + evmc::VM& m_vm; + +public: + explicit BlockchainGTest(fs::path json_test_file, evmc::VM& vm) noexcept + : m_json_test_file{std::move(json_test_file)}, m_vm{vm} + {} + + void TestBody() final + { + std::ifstream f{m_json_test_file}; + + try + { + evmone::test::run_blockchain_tests(evmone::test::load_blockchain_tests(f), m_vm); + } + catch (const evmone::test::UnsupportedTestFeature& ex) + { + GTEST_SKIP() << ex.what(); + } + } +}; + +void register_test(const std::string& suite_name, const fs::path& file, evmc::VM& vm) +{ + testing::RegisterTest(suite_name.c_str(), file.stem().string().c_str(), nullptr, nullptr, + file.string().c_str(), 0, + [file, &vm]() -> testing::Test* { return new BlockchainGTest(file, vm); }); +} + +void register_test_files(const fs::path& root, evmc::VM& vm) +{ + if (is_directory(root)) + { + std::vector test_files; + std::copy_if(fs::recursive_directory_iterator{root}, fs::recursive_directory_iterator{}, + std::back_inserter(test_files), [](const fs::directory_entry& entry) { + return entry.is_regular_file() && entry.path().extension() == ".json"; + }); + std::sort(test_files.begin(), test_files.end()); + + for (const auto& p : test_files) + register_test(fs::relative(p, root).parent_path().string(), p, vm); + } + else // Treat as a file. + { + register_test(root.parent_path().string(), root, vm); + } +} +} // namespace + + +int main(int argc, char* argv[]) +{ + try + { + testing::InitGoogleTest(&argc, argv); // Process GoogleTest flags. + + CLI::App app{"evmone blockchain test runner"}; + + app.set_version_flag("--version", "evmone-blockchaintest " EVMONE_VERSION); + + std::vector paths; + app.add_option("path", paths, "Path to test file or directory") + ->required() + ->check(CLI::ExistingPath); + + bool trace_flag = false; + app.add_flag("--trace", trace_flag, "Enable EVM tracing"); + + CLI11_PARSE(app, argc, argv); + + evmc::VM vm{evmc_create_evmone()}; + + if (trace_flag) + vm.set_option("trace", "1"); + + for (const auto& p : paths) + register_test_files(p, vm); + + return RUN_ALL_TESTS(); + } + catch (const std::exception& ex) + { + std::cerr << ex.what() << "\n"; + return -1; + } +} diff --git a/test/blockchaintest/blockchaintest.hpp b/test/blockchaintest/blockchaintest.hpp new file mode 100644 index 00000000..04412d0e --- /dev/null +++ b/test/blockchaintest/blockchaintest.hpp @@ -0,0 +1,74 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "../state/bloom_filter.hpp" +#include "../state/state.hpp" +#include "../state/test_state.hpp" +#include "../utils/utils.hpp" +#include +#include +#include + +namespace evmone::test +{ +struct UnsupportedTestFeature : std::runtime_error +{ + using runtime_error::runtime_error; +}; + +// https://ethereum.org/en/developers/docs/blocks/ +struct BlockHeader +{ + hash256 parent_hash; + address coinbase; + hash256 state_root; + hash256 receipts_root; + state::BloomFilter logs_bloom; + int64_t difficulty; + bytes32 prev_randao; + int64_t block_number; + int64_t gas_limit; + int64_t gas_used; + int64_t timestamp; + bytes extra_data; + uint64_t base_fee_per_gas; + hash256 hash; + hash256 transactions_root; + hash256 withdrawal_root; + hash256 parent_beacon_block_root; + uint64_t excess_blob_gas; +}; + +struct TestBlock +{ + state::BlockInfo block_info; + std::vector transactions; + + BlockHeader expected_block_header; +}; + +struct BlockchainTest +{ + struct Expectation + { + hash256 last_block_hash; + std::variant post_state; + }; + + std::string name; + + std::vector test_blocks; + BlockHeader genesis_block_header; + TestState pre_state; + RevisionSchedule rev; + + Expectation expectation; +}; + +std::vector load_blockchain_tests(std::istream& input); + +void run_blockchain_tests(std::span tests, evmc::VM& vm); + +} // namespace evmone::test diff --git a/test/blockchaintest/blockchaintest_loader.cpp b/test/blockchaintest/blockchaintest_loader.cpp new file mode 100644 index 00000000..b1f0cbac --- /dev/null +++ b/test/blockchaintest/blockchaintest_loader.cpp @@ -0,0 +1,150 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../statetest/statetest.hpp" +#include "../utils/utils.hpp" +#include "blockchaintest.hpp" + +namespace evmone::test +{ + +namespace +{ +template +T load_if_exists(const json::json& j, std::string_view key) +{ + if (const auto it = j.find(key); it != j.end()) + return from_json(*it); + return {}; +} +} // namespace + +template <> +BlockHeader from_json(const json::json& j) +{ + return { + .parent_hash = from_json(j.at("parentHash")), + .coinbase = from_json
(j.at("coinbase")), + .state_root = from_json(j.at("stateRoot")), + .receipts_root = from_json(j.at("receiptTrie")), + .logs_bloom = state::bloom_filter_from_bytes(from_json(j.at("bloom"))), + .difficulty = load_if_exists(j, "difficulty"), + .prev_randao = load_if_exists(j, "mixHash"), + .block_number = from_json(j.at("number")), + .gas_limit = from_json(j.at("gasLimit")), + .gas_used = from_json(j.at("gasUsed")), + .timestamp = from_json(j.at("timestamp")), + .extra_data = from_json(j.at("extraData")), + .base_fee_per_gas = load_if_exists(j, "baseFeePerGas"), + .hash = from_json(j.at("hash")), + .transactions_root = from_json(j.at("transactionsTrie")), + .withdrawal_root = load_if_exists(j, "withdrawalsRoot"), + .parent_beacon_block_root = load_if_exists(j, "parentBeaconBlockRoot"), + .excess_blob_gas = load_if_exists(j, "excessBlobGas"), + }; +} + +static TestBlock load_test_block(const json::json& j, evmc_revision rev) +{ + using namespace state; + TestBlock tb; + + if (const auto it = j.find("blockHeader"); it != j.end()) + { + tb.expected_block_header = from_json(*it); + tb.block_info.number = tb.expected_block_header.block_number; + tb.block_info.timestamp = tb.expected_block_header.timestamp; + tb.block_info.gas_limit = tb.expected_block_header.gas_limit; + tb.block_info.coinbase = tb.expected_block_header.coinbase; + tb.block_info.difficulty = tb.expected_block_header.difficulty; + tb.block_info.prev_randao = tb.expected_block_header.prev_randao; + tb.block_info.base_fee = tb.expected_block_header.base_fee_per_gas; + tb.block_info.parent_beacon_block_root = tb.expected_block_header.parent_beacon_block_root; + tb.block_info.blob_base_fee = + compute_blob_gas_price(tb.expected_block_header.excess_blob_gas); + + // Override prev_randao with difficulty pre-Merge + if (rev < EVMC_PARIS) + { + tb.block_info.prev_randao = + intx::be::store(intx::uint256{tb.block_info.difficulty}); + } + } + + if (const auto it = j.find("expectException"); it != j.end()) + { + // TODO: Add support for invalid blocks. + throw UnsupportedTestFeature("tests with invalid blocks are not supported"); + } + + if (const auto it = j.find("transactionSequence"); it != j.end()) + { + // TODO: Add support for invalid blocks. + throw UnsupportedTestFeature("tests with invalid transactions are not supported"); + } + + if (const auto it = j.find("uncleHeaders"); it != j.end()) + { + const auto current_block_number = tb.block_info.number; + for (const auto& ommer : *it) + { + tb.block_info.ommers.push_back({from_json
(ommer.at("coinbase")), + static_cast( + current_block_number - from_json(ommer.at("number")))}); + } + } + + if (auto it = j.find("withdrawals"); it != j.end()) + { + for (const auto& withdrawal : *it) + tb.block_info.withdrawals.emplace_back(from_json(withdrawal)); + } + + if (auto it = j.find("transactions"); it != j.end()) + { + for (const auto& tx : *it) + tb.transactions.emplace_back(from_json(tx)); + } + + return tb; +} + +namespace +{ +BlockchainTest load_blockchain_test_case(const std::string& name, const json::json& j) +{ + using namespace state; + + BlockchainTest bt; + bt.name = name; + bt.genesis_block_header = from_json(j.at("genesisBlockHeader")); + bt.pre_state = from_json(j.at("pre")); + bt.rev = to_rev_schedule(j.at("network").get()); + + for (const auto& el : j.at("blocks")) + bt.test_blocks.emplace_back(load_test_block(el, bt.rev.get_revision(0))); + + bt.expectation.last_block_hash = from_json(j.at("lastblockhash")); + + if (const auto it = j.find("postState"); it != j.end()) + bt.expectation.post_state = from_json(*it); + else if (const auto it_hash = j.find("postStateHash"); it_hash != j.end()) + bt.expectation.post_state = from_json(*it_hash); + + return bt; +} +} // namespace + +static void from_json(const json::json& j, std::vector& o) +{ + for (const auto& elem_it : j.items()) + o.emplace_back(load_blockchain_test_case(elem_it.key(), elem_it.value())); +} + +std::vector load_blockchain_tests(std::istream& input) +{ + return json::json::parse(input).get>(); +} + +} // namespace evmone::test diff --git a/test/blockchaintest/blockchaintest_runner.cpp b/test/blockchaintest/blockchaintest_runner.cpp new file mode 100644 index 00000000..b80f83e3 --- /dev/null +++ b/test/blockchaintest/blockchaintest_runner.cpp @@ -0,0 +1,196 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../state/mpt_hash.hpp" +#include "../state/rlp.hpp" +#include "../state/state.hpp" +#include "../state/system_contracts.hpp" +#include "../test/statetest/statetest.hpp" +#include "blockchaintest.hpp" +#include + +namespace evmone::test +{ + +struct RejectedTransaction +{ + hash256 hash; + size_t index; + std::string message; +}; + +struct TransitionResult +{ + std::vector receipts; + std::vector rejected; + int64_t gas_used; + state::BloomFilter bloom; +}; + +namespace +{ +TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::BlockInfo& block, + const std::vector& txs, evmc_revision rev, + std::optional block_reward) +{ + state::system_call(state, block, rev, vm); + + std::vector txs_logs; + int64_t block_gas_left = block.gas_limit; + int64_t blob_gas_left = state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK; + + std::vector rejected_txs; + std::vector receipts; + + int64_t cumulative_gas_used = 0; + + for (size_t i = 0; i < txs.size(); ++i) + { + const auto& tx = txs[i]; + + const auto computed_tx_hash = keccak256(rlp::encode(tx)); + auto res = state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + + if (holds_alternative(res)) + { + const auto ec = std::get(res); + rejected_txs.push_back({computed_tx_hash, i, ec.message()}); + } + else + { + auto& receipt = get(res); + + const auto& tx_logs = receipt.logs; + + txs_logs.insert(txs_logs.end(), tx_logs.begin(), tx_logs.end()); + cumulative_gas_used += receipt.gas_used; + receipt.cumulative_gas_used = cumulative_gas_used; + if (rev < EVMC_BYZANTIUM) + receipt.post_state = state::mpt_hash(TestState{state}); + + block_gas_left -= receipt.gas_used; + blob_gas_left -= tx.blob_gas_used(); + receipts.emplace_back(std::move(receipt)); + } + } + + state::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); + + const auto bloom = compute_bloom_filter(receipts); + return {std::move(receipts), std::move(rejected_txs), cumulative_gas_used, bloom}; +} + +std::optional mining_reward(evmc_revision rev) noexcept +{ + if (rev < EVMC_BYZANTIUM) + return 5'000000000'000000000; + if (rev < EVMC_CONSTANTINOPLE) + return 3'000000000'000000000; + if (rev < EVMC_PARIS) + return 2'000000000'000000000; + return std::nullopt; +} + +std::string print_state(const TestState& s) +{ + std::stringstream out; + + for (const auto& [key, acc] : s) + { + out << key << " : \n"; + out << "\tnonce : " << acc.nonce << "\n"; + out << "\tbalance : " << hex0x(acc.balance) << "\n"; + out << "\tcode : " << hex0x(acc.code) << "\n"; + + if (!acc.storage.empty()) + { + out << "\tstorage : \n"; + for (const auto& [s_key, val] : acc.storage) + { + if (!is_zero(val)) // Skip 0 values. + out << "\t\t" << s_key << " : " << hex0x(val) << "\n"; + } + } + } + + return out.str(); +} +} // namespace + +void run_blockchain_tests(std::span tests, evmc::VM& vm) +{ + for (size_t case_index = 0; case_index != tests.size(); ++case_index) + { + const auto& c = tests[case_index]; + SCOPED_TRACE(std::string{evmc::to_string(c.rev.get_revision(0))} + '/' + + std::to_string(case_index) + '/' + c.name); + + // Validate the genesis block header. + EXPECT_EQ(c.genesis_block_header.block_number, 0); + EXPECT_EQ(c.genesis_block_header.gas_used, 0); + EXPECT_EQ(c.genesis_block_header.transactions_root, state::EMPTY_MPT_HASH); + EXPECT_EQ(c.genesis_block_header.receipts_root, state::EMPTY_MPT_HASH); + EXPECT_EQ(c.genesis_block_header.withdrawal_root, + c.rev.get_revision(c.genesis_block_header.timestamp) >= EVMC_SHANGHAI ? + state::EMPTY_MPT_HASH : + bytes32{}); + EXPECT_EQ(c.genesis_block_header.logs_bloom, bytes_view{state::BloomFilter{}}); + + auto state = c.pre_state.to_intra_state(); + + std::unordered_map known_block_hashes{ + {c.genesis_block_header.block_number, c.genesis_block_header.hash}}; + + for (const auto& test_block : c.test_blocks) + { + auto bi = test_block.block_info; + bi.known_block_hashes = known_block_hashes; + + const auto rev = c.rev.get_revision(bi.timestamp); + + const auto res = + apply_block(state, vm, bi, test_block.transactions, rev, mining_reward(rev)); + + known_block_hashes[test_block.expected_block_header.block_number] = + test_block.expected_block_header.hash; + + SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index) + + '/' + c.name + '/' + std::to_string(test_block.block_info.number)); + + EXPECT_EQ( + state::mpt_hash(TestState{state}), test_block.expected_block_header.state_root); + + if (rev >= EVMC_SHANGHAI) + { + EXPECT_EQ(state::mpt_hash(test_block.block_info.withdrawals), + test_block.expected_block_header.withdrawal_root); + } + + EXPECT_EQ(state::mpt_hash(test_block.transactions), + test_block.expected_block_header.transactions_root); + EXPECT_EQ( + state::mpt_hash(res.receipts), test_block.expected_block_header.receipts_root); + EXPECT_EQ(res.gas_used, test_block.expected_block_header.gas_used); + EXPECT_EQ( + bytes_view{res.bloom}, bytes_view{test_block.expected_block_header.logs_bloom}); + + // TODO: Add difficulty calculation verification. + } + + const TestState post{state}; + const auto expected_post_hash = + std::holds_alternative(c.expectation.post_state) ? + state::mpt_hash(std::get(c.expectation.post_state)) : + std::get(c.expectation.post_state); + EXPECT_TRUE(state::mpt_hash(post) == expected_post_hash) + << "Result state:\n" + << print_state(post) + << (std::holds_alternative(c.expectation.post_state) ? + "\n\nExpected state:\n" + + print_state(std::get(c.expectation.post_state)) : + ""); + } +} + +} // namespace evmone::test diff --git a/test/eofparse/eofparse.cpp b/test/eofparse/eofparse.cpp index 6fd9937b..ee28e750 100644 --- a/test/eofparse/eofparse.cpp +++ b/test/eofparse/eofparse.cpp @@ -2,6 +2,7 @@ // Copyright 2023 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include @@ -35,10 +36,19 @@ std::optional from_hex_skip_nonalnum(InputIterator begin, InputIter } // namespace -int main() +int main(int argc, char* argv[]) { try { + CLI::App app{"evmone eofparse tool"}; + const auto& initcode_flag = + *app.add_flag("--initcode", "Validate code as initcode containers"); + + app.parse(argc, argv); + const auto container_kind = + initcode_flag ? evmone::ContainerKind::initcode : evmone::ContainerKind::runtime; + + int num_errors = 0; for (std::string line; std::getline(std::cin, line);) { if (line.empty() || line.starts_with('#')) @@ -48,14 +58,16 @@ int main() if (!o) { std::cout << "err: invalid hex\n"; + ++num_errors; continue; } const auto& eof = *o; - const auto err = evmone::validate_eof(EVMC_CANCUN, eof); + const auto err = evmone::validate_eof(EVMC_PRAGUE, container_kind, eof); if (err != evmone::EOFValidationError::success) { std::cout << "err: " << evmone::get_error_message(err) << "\n"; + ++num_errors; continue; } @@ -69,11 +81,11 @@ int main() } std::cout << "\n"; } - return 0; + return num_errors; } catch (const std::exception& ex) { std::cerr << ex.what() << "\n"; - return 1; + return -1; } } diff --git a/test/eofparsefuzz/eofparsefuzz.cpp b/test/eofparsefuzz/eofparsefuzz.cpp index e786abc2..139200e9 100644 --- a/test/eofparsefuzz/eofparsefuzz.cpp +++ b/test/eofparsefuzz/eofparsefuzz.cpp @@ -7,7 +7,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t data_size) noexcept { const evmone::bytes_view eof{data, data_size}; - if (evmone::validate_eof(EVMC_CANCUN, eof) == evmone::EOFValidationError::success) + if (evmone::validate_eof(EVMC_PRAGUE, evmone::ContainerKind::runtime, eof) == + evmone::EOFValidationError::success) (void)evmone::read_valid_eof1_header(eof); return 0; } diff --git a/test/eoftest/.clang-tidy b/test/eoftest/.clang-tidy new file mode 100644 index 00000000..efc628c8 --- /dev/null +++ b/test/eoftest/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: > + -clang-analyzer-cplusplus.NewDeleteLeaks diff --git a/test/eoftest/CMakeLists.txt b/test/eoftest/CMakeLists.txt new file mode 100644 index 00000000..25df1d0e --- /dev/null +++ b/test/eoftest/CMakeLists.txt @@ -0,0 +1,12 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +add_executable(evmone-eoftest) +target_link_libraries(evmone-eoftest PRIVATE evmone evmone::testutils nlohmann_json::nlohmann_json GTest::gtest) +target_include_directories(evmone-eoftest PRIVATE ${evmone_private_include_dir}) +target_sources( + evmone-eoftest PRIVATE + eoftest.cpp + eoftest_runner.cpp +) diff --git a/test/eoftest/eoftest.cpp b/test/eoftest/eoftest.cpp new file mode 100644 index 00000000..df06160b --- /dev/null +++ b/test/eoftest/eoftest.cpp @@ -0,0 +1,86 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + + +#include "eoftest.hpp" +#include +#include +#include + +namespace fs = std::filesystem; + +namespace +{ + +class EOFTest : public testing::Test +{ + fs::path m_json_test_file; + +public: + explicit EOFTest(fs::path json_test_file) noexcept : m_json_test_file{std::move(json_test_file)} + {} + + void TestBody() final + { + std::ifstream f{m_json_test_file}; + evmone::test::run_eof_test(f); + } +}; + +void register_test(const std::string& suite_name, const fs::path& file) +{ + testing::RegisterTest(suite_name.c_str(), file.stem().string().c_str(), nullptr, nullptr, + file.string().c_str(), 0, [file]() -> testing::Test* { return new EOFTest(file); }); +} + +void register_test_files(const fs::path& root) +{ + if (is_directory(root)) + { + std::vector test_files; + std::copy_if(fs::recursive_directory_iterator{root}, fs::recursive_directory_iterator{}, + std::back_inserter(test_files), [](const fs::directory_entry& entry) { + return entry.is_regular_file() && entry.path().extension() == ".json"; + }); + std::sort(test_files.begin(), test_files.end()); + + for (const auto& p : test_files) + register_test(fs::relative(p, root).parent_path().string(), p); + } + else // Treat as a file. + { + register_test(root.parent_path().string(), root); + } +} + +} // namespace + + +int main(int argc, char* argv[]) +{ + try + { + testing::InitGoogleTest(&argc, argv); + CLI::App app{"evmone eof test runner"}; + + std::vector paths; + app.add_option("path", paths, "Path to test file or directory") + ->required() + ->check(CLI::ExistingPath); + + CLI11_PARSE(app, argc, argv); + + for (const auto& p : paths) + { + register_test_files(p); + } + + return RUN_ALL_TESTS(); + } + catch (const std::exception& ex) + { + std::cerr << ex.what() << "\n"; + return -1; + } +} diff --git a/test/eoftest/eoftest.hpp b/test/eoftest/eoftest.hpp new file mode 100644 index 00000000..e98f76d2 --- /dev/null +++ b/test/eoftest/eoftest.hpp @@ -0,0 +1,13 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::test +{ + +void run_eof_test(std::istream& input); + +} // namespace evmone::test diff --git a/test/eoftest/eoftest_runner.cpp b/test/eoftest/eoftest_runner.cpp new file mode 100644 index 00000000..62e685ea --- /dev/null +++ b/test/eoftest/eoftest_runner.cpp @@ -0,0 +1,102 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/utils.hpp" +#include "eoftest.hpp" +#include +#include +#include +#include + +namespace json = nlohmann; + +namespace evmone::test +{ + +namespace +{ +struct EOFValidationTest +{ + struct Case + { + struct Expectation + { + evmc_revision rev = EVMC_PRAGUE; + bool result = false; + }; + std::string name; + evmc::bytes code; + ContainerKind kind = ContainerKind::runtime; + std::vector expectations; + }; + std::string name; + std::unordered_map cases; +}; + +void from_json(const json::json& j, EOFValidationTest::Case& o) +{ + const auto op_code = evmc::from_hex(j.at("code").get()); + if (!op_code) + throw std::invalid_argument{"code is invalid hex string"}; + o.code = *op_code; + + if (const auto it_kind = j.find("containerKind"); it_kind != j.end()) + { + if (it_kind->get() == "INITCODE") + o.kind = ContainerKind::initcode; + } + + for (const auto& [rev, result] : j.at("results").items()) + { + o.expectations.push_back({to_rev(rev), result.at("result").get()}); + } +} + +void from_json(const json::json& j, EOFValidationTest& o) +{ + if (!j.is_object() || j.empty()) + throw std::invalid_argument{"JSON test must be an object with single key of the test name"}; + + for (const auto& [name, test] : j.at("vectors").items()) + { + o.cases.emplace(name, test.get()); + } +} + +void from_json(const json::json& j, std::vector& o) +{ + for (const auto& elem_it : j.items()) + { + auto test = elem_it.value().get(); + test.name = elem_it.key(); + o.emplace_back(std::move(test)); + } +} + +std::vector load_eof_tests(std::istream& input) +{ + return json::json::parse(input).get>(); +} + +} // namespace + +void run_eof_test(std::istream& input) +{ + const auto tests = evmone::test::load_eof_tests(input); + for (const auto& test : tests) + { + for (const auto& [name, cases] : test.cases) + { + for (const auto& expectation : cases.expectations) + { + const auto result = evmone::validate_eof(expectation.rev, cases.kind, cases.code); + const bool b_result = (result == EOFValidationError::success); + EXPECT_EQ(b_result, expectation.result) + << name << " " << expectation.rev << " " << hex(cases.code); + } + } + } +} + +} // namespace evmone::test diff --git a/test/fuzzer/fuzzer.cpp b/test/fuzzer/fuzzer.cpp index 11e6ecc4..ca3f0154 100644 --- a/test/fuzzer/fuzzer.cpp +++ b/test/fuzzer/fuzzer.cpp @@ -6,11 +6,12 @@ #include #include #include - #include #include #include +using namespace evmone::test; + inline std::ostream& operator<<(std::ostream& os, const evmc_address& addr) { return os << hex({addr.bytes, sizeof(addr.bytes)}); diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 6ac5f908..dc1ebc2b 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -7,7 +7,7 @@ set(PREFIX ${PROJECT_NAME}/integration) get_target_property(EVMONE_LIB_TYPE evmone TYPE) if(EVMONE_LIB_TYPE STREQUAL SHARED_LIBRARY) - add_test(NAME ${PREFIX}/histogram COMMAND $ --vm $,histogram run 6000808080800101010200) + add_test(NAME ${PREFIX}/histogram COMMAND evmc::tool --vm $,histogram run 6000808080800101010200) set_tests_properties( ${PREFIX}/histogram PROPERTIES PASS_REGULAR_EXPRESSION "--- # HISTOGRAM depth=0 @@ -19,21 +19,42 @@ PUSH1,1 DUP1,4 ") - add_test(NAME ${PREFIX}/trace COMMAND $ --vm $,trace run 60006002800103) + add_test(NAME ${PREFIX}/trace COMMAND evmc::tool --vm $,trace run 60006002800103) set_tests_properties( ${PREFIX}/trace PROPERTIES PASS_REGULAR_EXPRESSION - "{\"depth\":0,\"rev\":\"Shanghai\",\"static\":false} -{\"pc\":0,\"op\":96,\"opName\":\"PUSH1\",\"gas\":0xf4240,\"stack\":\\[\\],\"memorySize\":0} -{\"pc\":2,\"op\":96,\"opName\":\"PUSH1\",\"gas\":0xf423d,\"stack\":\\[\"0x0\"\\],\"memorySize\":0} -{\"pc\":4,\"op\":128,\"opName\":\"DUP1\",\"gas\":0xf423a,\"stack\":\\[\"0x0\",\"0x2\"\\],\"memorySize\":0} -{\"pc\":5,\"op\":1,\"opName\":\"ADD\",\"gas\":0xf4237,\"stack\":\\[\"0x0\",\"0x2\",\"0x2\"\\],\"memorySize\":0} -{\"pc\":6,\"op\":3,\"opName\":\"SUB\",\"gas\":0xf4234,\"stack\":\\[\"0x0\",\"0x4\"\\],\"memorySize\":0} -{\"error\":null,\"gas\":0xf4231,\"gasUsed\":0xf,\"output\":\"\"} + "\"pc\":0,\"op\":96,\"gas\":\"0xf4240\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\\],\"depth\":1,\"refund\":0,\"opName\":\"PUSH1\"} +{\"pc\":2,\"op\":96,\"gas\":\"0xf423d\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\"\\],\"depth\":1,\"refund\":0,\"opName\":\"PUSH1\"} +{\"pc\":4,\"op\":128,\"gas\":\"0xf423a\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x2\"\\],\"depth\":1,\"refund\":0,\"opName\":\"DUP1\"} +{\"pc\":5,\"op\":1,\"gas\":\"0xf4237\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x2\",\"0x2\"\\],\"depth\":1,\"refund\":0,\"opName\":\"ADD\"} +{\"pc\":6,\"op\":3,\"gas\":\"0xf4234\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x4\"\\],\"depth\":1,\"refund\":0,\"opName\":\"SUB\"} ") + add_test(NAME ${PREFIX}/validate_eof COMMAND evmc::tool --vm $,validate_eof run --rev 13 EF0001) + set_tests_properties( + ${PREFIX}/validate_eof PROPERTIES PASS_REGULAR_EXPRESSION + "contract validation failure") + + add_test(NAME ${PREFIX}/validate_eof_success COMMAND evmc::tool --vm $,validate_eof run --rev 13 EF00010100040200010001040000000080000000) + set_tests_properties( + ${PREFIX}/validate_eof_success PROPERTIES PASS_REGULAR_EXPRESSION + "Result: success") + + add_test(NAME ${PREFIX}/validate_eof_create COMMAND evmc::tool --vm $,validate_eof run --rev 13 --create EF00010100040200010001040000000080000000) + set_tests_properties( + ${PREFIX}/validate_eof_create PROPERTIES PASS_REGULAR_EXPRESSION + "contract validation failure") + + add_test(NAME ${PREFIX}/validate_eof_create_success COMMAND evmc::tool --vm $,validate_eof run --rev 13 --create EF00010100040200010004030001001404000000008000025F5FEE00EF00010100040200010001040000000080000000) + set_tests_properties( + ${PREFIX}/validate_eof_create_success PROPERTIES PASS_REGULAR_EXPRESSION + "Result: success") + endif() +add_subdirectory(eofparse) +add_subdirectory(export) add_subdirectory(statetest) +add_subdirectory(t8n) get_property(ALL_TESTS DIRECTORY PROPERTY TESTS) set_tests_properties(${ALL_TESTS} PROPERTIES ENVIRONMENT LLVM_PROFILE_FILE=${CMAKE_BINARY_DIR}/integration-%p.profraw) diff --git a/test/integration/eofparse/CMakeLists.txt b/test/integration/eofparse/CMakeLists.txt new file mode 100644 index 00000000..57e5374d --- /dev/null +++ b/test/integration/eofparse/CMakeLists.txt @@ -0,0 +1,25 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2024 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +set(PREFIX ${PROJECT_NAME}/integration/eofparse) + +string(REPLACE ";" " " CROSSCOMPILING_EMULATOR "${CMAKE_CROSSCOMPILING_EMULATOR}") + +add_test(NAME ${PREFIX}/minimal_eof COMMAND sh -c "echo EF0001.010004 0200010001 040000 00,00800000 FE | ${CROSSCOMPILING_EMULATOR} $") +set_tests_properties(${PREFIX}/minimal_eof PROPERTIES PASS_REGULAR_EXPRESSION "OK fe") + +add_test(NAME ${PREFIX}/two_code_sections COMMAND sh -c "echo EF0001 010008 02000200030001 040000 00 00800000,00800000 E50001,00 | ${CROSSCOMPILING_EMULATOR} $") +set_tests_properties(${PREFIX}/two_code_sections PROPERTIES PASS_REGULAR_EXPRESSION "OK e50001,00") + +add_test(NAME ${PREFIX}/eof_version_unknown COMMAND sh -c "echo EF00.FF | ${CROSSCOMPILING_EMULATOR} $") +set_tests_properties(${PREFIX}/eof_version_unknown PROPERTIES PASS_REGULAR_EXPRESSION "err: eof_version_unknown") + +add_test(NAME ${PREFIX}/invalid_hex COMMAND sh -c "echo gaga | ${CROSSCOMPILING_EMULATOR} $") +set_tests_properties(${PREFIX}/invalid_hex PROPERTIES PASS_REGULAR_EXPRESSION "err: invalid hex") + +add_test(NAME ${PREFIX}/example_input_file COMMAND sh -c "${CROSSCOMPILING_EMULATOR} $ <${CMAKE_CURRENT_SOURCE_DIR}/two_errors.txt") +set_tests_properties(${PREFIX}/example_input_file PROPERTIES PASS_REGULAR_EXPRESSION "OK 00\nerr: type_section_missing\nerr: no_terminating_instruction") + +add_test(NAME ${PREFIX}/exit_code COMMAND sh -c "${CROSSCOMPILING_EMULATOR} $ <${CMAKE_CURRENT_SOURCE_DIR}/two_errors.txt >/dev/null; echo $?") +set_tests_properties(${PREFIX}/exit_code PROPERTIES PASS_REGULAR_EXPRESSION "2") diff --git a/test/integration/eofparse/two_errors.txt b/test/integration/eofparse/two_errors.txt new file mode 100644 index 00000000..7452cba0 --- /dev/null +++ b/test/integration/eofparse/two_errors.txt @@ -0,0 +1,10 @@ +# Example input file to eofparse tool. Two of the following lines contain EOF validation errors. + +# Minimal valid EOF container. +EF0001 010004 0200010001 040000 00 00800000 00 + +# Mandatory sections missing +EF0001 00 + +# Code section not terminated +EF0001 010004 0200010001 040000 00 00800000 5b diff --git a/test/integration/export/CMakeLists.txt b/test/integration/export/CMakeLists.txt new file mode 100644 index 00000000..d18eb7b8 --- /dev/null +++ b/test/integration/export/CMakeLists.txt @@ -0,0 +1,59 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +# Commands and tests for exporting evmone's tests to JSON formats + +set(PREFIX ${PREFIX}/export) +set(EXPORT_DIR fixtures) + +add_test( + # Clean the export directory before exporting to delete potentially outdated tests. + NAME ${PREFIX}/clean_export_dir + COMMAND ${CMAKE_COMMAND} -E rm -rf ${EXPORT_DIR} +) +set_tests_properties( + ${PREFIX}/clean_export_dir PROPERTIES + FIXTURES_SETUP EXPORT_PREPARATION +) + +add_test( + NAME ${PREFIX}/export_state_tests + COMMAND evmone-unittests --gtest_filter=state_transition.* +) +set_tests_properties( + ${PREFIX}/export_state_tests PROPERTIES + ENVIRONMENT EVMONE_EXPORT_TESTS=${EXPORT_DIR}/state_tests + FIXTURES_SETUP EXPORT_STATE_TESTS + FIXTURES_REQUIRED EXPORT_PREPARATION +) + +add_test( + NAME ${PREFIX}/execute_exported_state_tests + # TODO: Broken exported tests are filtered out. + COMMAND evmone-statetest ${EXPORT_DIR}/state_tests --gtest_filter=-*block.* +) +set_tests_properties( + ${PREFIX}/execute_exported_state_tests PROPERTIES + FIXTURES_REQUIRED EXPORT_STATE_TESTS +) + +add_test( + NAME ${PREFIX}/export_eof_tests + COMMAND evmone-unittests --gtest_filter=eof_validation.* +) +set_tests_properties( + ${PREFIX}/export_eof_tests PROPERTIES + ENVIRONMENT EVMONE_EXPORT_TESTS=${EXPORT_DIR}/eof_tests + FIXTURES_SETUP EXPORT_EOF_TESTS + FIXTURES_REQUIRED EXPORT_PREPARATION +) + +add_test( + NAME ${PREFIX}/execute_exported_eof_tests + COMMAND evmone-eoftest ${EXPORT_DIR}/eof_tests +) +set_tests_properties( + ${PREFIX}/execute_exported_eof_tests PROPERTIES + FIXTURES_REQUIRED EXPORT_EOF_TESTS +) diff --git a/test/integration/statetest/CMakeLists.txt b/test/integration/statetest/CMakeLists.txt index ec3bbe96..8f1c1eed 100644 --- a/test/integration/statetest/CMakeLists.txt +++ b/test/integration/statetest/CMakeLists.txt @@ -8,6 +8,7 @@ set(PREFIX ${PREFIX}/statetest) set(TESTS1 ${CMAKE_CURRENT_SOURCE_DIR}/tests1) set(TESTS2 ${CMAKE_CURRENT_SOURCE_DIR}/tests2) set(TESTS_EOF ${CMAKE_CURRENT_SOURCE_DIR}/eof) +set(TESTS_TX ${CMAKE_CURRENT_SOURCE_DIR}/tx) add_test( NAME ${PREFIX}/no_arguments @@ -29,7 +30,7 @@ B\. T SuiteA\. test1 - test2 + test2_multi ]] ) @@ -56,13 +57,13 @@ B\. T SuiteA\. test1 - test2 + test2_multi test1 .*test/integration/statetest/tests1/B\. T \. test1 - test2 + test2_multi ]] ) @@ -75,13 +76,37 @@ set_tests_properties( PASS_REGULAR_EXPRESSION "path: Path does not exist: invalid\\.json" ) +add_test( + NAME ${PREFIX}/multi_test + COMMAND evmone-statetest ${TESTS1}/SuiteA/test2_multi.json +) +set_tests_properties( + ${PREFIX}/multi_test PROPERTIES + # Make sure both tests in the file are executed (both should fail). + PASS_REGULAR_EXPRESSION "test_case_1.*test_case_2" +) + add_test( NAME ${PREFIX}/trace COMMAND evmone-statetest ${TESTS1}/SuiteA/test1.json --trace ) +set(EXPECTED_TRACE [[ +{"pc":0,"op":96,"gas":"0x5c878","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x5c875","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":1,"gas":"0x5c872","gasCost":"0x3","memSize":0,"stack":["0x1","0x1"],"depth":1,"refund":0,"opName":"ADD"} +{"pc":5,"op":96,"gas":"0x5c86f","gasCost":"0x3","memSize":0,"stack":["0x2"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":7,"op":85,"gas":"0x5c86c","gasCost":"0x0","memSize":0,"stack":["0x2","0x0"],"depth":1,"refund":0,"opName":"SSTORE"} +{"pc":8,"op":0,"gas":"0x57218","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"pass":true,"gasUsed":"0xa868","stateRoot":"0xe8010ce590f401c9d61fef8ab05bea9bcec24281b795e5868809bc4e515aa530"} +]]) +# Escape regex special characters. +string(REPLACE "{" "\\{" EXPECTED_TRACE ${EXPECTED_TRACE}) +string(REPLACE "}" "\\}" EXPECTED_TRACE ${EXPECTED_TRACE}) +string(REPLACE "[" "\\[" EXPECTED_TRACE ${EXPECTED_TRACE}) +string(REPLACE "]" "\\]" EXPECTED_TRACE ${EXPECTED_TRACE}) set_tests_properties( ${PREFIX}/trace PROPERTIES - PASS_REGULAR_EXPRESSION [=[\{"pc":4,"op":1,"opName":"ADD","gas":0x5c872,"stack":\["0x1","0x1"\],"memorySize":0\}]=] + PASS_REGULAR_EXPRESSION ${EXPECTED_TRACE} ) add_test( @@ -92,3 +117,12 @@ set_tests_properties( ${PREFIX}/invalid_eof_in_state PROPERTIES PASS_REGULAR_EXPRESSION "EOF container at 0x0000000000000000000000000000000000bade0f is invalid" ) + +add_test( + NAME ${PREFIX}/tx_invalid_nonce + COMMAND evmone-statetest ${TESTS_TX}/invalid_nonce.json +) +set_tests_properties( + ${PREFIX}/tx_invalid_nonce PROPERTIES + PASS_REGULAR_EXPRESSION "unexpected invalid transaction: nonce too high" +) diff --git a/test/integration/statetest/eof/invalid_eof_in_state.json b/test/integration/statetest/eof/invalid_eof_in_state.json index db0384bd..05a3e6dc 100644 --- a/test/integration/statetest/eof/invalid_eof_in_state.json +++ b/test/integration/statetest/eof/invalid_eof_in_state.json @@ -11,7 +11,7 @@ "currentTimestamp": "0x03e8" }, "post": { - "Cancun": [ + "Prague": [ { "hash": "0xe8010ce590f401c9d61fef8ab05bea9bcec24281b795e5868809bc4e515aa530", "indexes": { diff --git a/test/integration/statetest/tests1/SuiteA/test2.json b/test/integration/statetest/tests1/SuiteA/test2.json deleted file mode 100644 index e69de29b..00000000 diff --git a/test/integration/statetest/tests1/SuiteA/test2_multi.json b/test/integration/statetest/tests1/SuiteA/test2_multi.json new file mode 100644 index 00000000..d396ff85 --- /dev/null +++ b/test/integration/statetest/tests1/SuiteA/test2_multi.json @@ -0,0 +1,96 @@ +{ + "test_case_1": { + "env": { + "currentBaseFee": "0x0a", + "currentCoinbase": "0x0000000000000000000000000000000000000000", + "currentDifficulty": "0x020000", + "currentGasLimit": "0xff112233445566", + "currentNumber": "0x01", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000", + "currentTimestamp": "0x03e8" + }, + "post": { + "London": [ + { + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + "pre": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "0x", + "nonce": "0x00", + "storage": {} + } + }, + "transaction": { + "data": [ + "0x" + ], + "gasLimit": [ + "0x061a80" + ], + "gasPrice": "0x0a", + "nonce": "0x00", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "to": "0x0000000000000000000000000000000000000000", + "value": [ + "0x1" + ] + } + }, + "test_case_2": { + "env": { + "currentBaseFee": "0x0a", + "currentCoinbase": "0x0000000000000000000000000000000000000000", + "currentDifficulty": "0x020000", + "currentGasLimit": "0xff112233445566", + "currentNumber": "0x01", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000", + "currentTimestamp": "0x03e8" + }, + "post": { + "London": [ + { + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + "pre": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "0x", + "nonce": "0x00", + "storage": {} + } + }, + "transaction": { + "data": [ + "0x" + ], + "gasLimit": [ + "0x061a80" + ], + "gasPrice": "0x0a", + "nonce": "0x00", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "to": "0x0000000000000000000000000000000000000000", + "value": [ + "0x2" + ] + } + } +} diff --git a/test/integration/statetest/tx/invalid_nonce.json b/test/integration/statetest/tx/invalid_nonce.json new file mode 100644 index 00000000..77fb0deb --- /dev/null +++ b/test/integration/statetest/tx/invalid_nonce.json @@ -0,0 +1,42 @@ +{ + "invalid_nonce": { + "env": { + "currentBaseFee": "0x0a", + "currentCoinbase": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty": "0x020000", + "currentGasLimit": "0xff112233445566", + "currentNumber": "0x01", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000", + "currentTimestamp": "0x03e8" + }, + "post": { + "Shanghai": [ + { + "hash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + "pre": {}, + "transaction": { + "data": [ + "0x" + ], + "gasLimit": [ + "0x00" + ], + "gasPrice": "0x0a", + "nonce": "0x01", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "to": "0x00", + "value": [ + "0x00" + ] + } + } +} diff --git a/test/integration/t8n/CMakeLists.txt b/test/integration/t8n/CMakeLists.txt new file mode 100644 index 00000000..653ea330 --- /dev/null +++ b/test/integration/t8n/CMakeLists.txt @@ -0,0 +1,59 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2024 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +# Integration tests for evmone-t8n. + +set(PREFIX ${PREFIX}/t8n) + +set(TEST_CASE cancun_create_tx) + +add_test( + NAME ${PREFIX}/${TEST_CASE} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_CASE} + COMMAND + evmone-t8n + --state.fork Cancun + --state.reward 0 + --state.chainid 1 + --input.alloc alloc.json + --input.txs txs.json + --input.env env.json + --output.basedir ${CMAKE_CURRENT_BINARY_DIR}/${TEST_CASE} + --output.result out.json + --output.alloc outAlloc.json + --output.errorlog error.json +) +set_tests_properties(${PREFIX}/${TEST_CASE} PROPERTIES FIXTURES_REQUIRED ${TEST_CASE}) + +add_test( + NAME ${PREFIX}/${TEST_CASE}/out.json + COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_CURRENT_BINARY_DIR}/${TEST_CASE}/out.json +) +string( + JOIN ".*" EXPECTED_OUT + # Create blob transaction should be rejected: + [=["error": "blob transaction must not be a create transaction"]=] +) +set_tests_properties( + ${PREFIX}/${TEST_CASE}/out.json PROPERTIES + FIXTURES_CLEANUP ${TEST_CASE} + PASS_REGULAR_EXPRESSION ${EXPECTED_OUT} +) + +add_test( + NAME ${PREFIX}/${TEST_CASE}/outAlloc.json + COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_CURRENT_BINARY_DIR}/${TEST_CASE}/outAlloc.json +) +string( + JOIN ".*" EXPECTED_OUT_ALLOC + # Beacon Roots system contract has new entry: + [=["0x000f3df6d732807ef1319fb7b8bb8522d0beac02": .*"storage": \{[^}]*"0x00000000000000000000000000000000000000000000000000000000000016ca": "0x0000000000000000000000000000000000000000000000000000000054c99069"]=] + # new account created with code 0x00: + [=["0x6295ee1b4f6dd65047762f924ecd367c17eabf8f": \{[^}]*"code": "0x00",]=] +) +set_tests_properties( + ${PREFIX}/${TEST_CASE}/outAlloc.json PROPERTIES + FIXTURES_CLEANUP ${TEST_CASE} + PASS_REGULAR_EXPRESSION ${EXPECTED_OUT_ALLOC} +) diff --git a/test/integration/t8n/cancun_create_tx/alloc.json b/test/integration/t8n/cancun_create_tx/alloc.json new file mode 100644 index 00000000..136ed097 --- /dev/null +++ b/test/integration/t8n/cancun_create_tx/alloc.json @@ -0,0 +1,15 @@ +{ + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "nonce": "0x01", + "balance": "0x00", + "storage": { + "0x12e2": "0x54c98c81" + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "", + "nonce": "0x00", + "balance": "0x02540be400" + } +} diff --git a/test/integration/t8n/cancun_create_tx/env.json b/test/integration/t8n/cancun_create_tx/env.json new file mode 100644 index 00000000..8d582b87 --- /dev/null +++ b/test/integration/t8n/cancun_create_tx/env.json @@ -0,0 +1,6 @@ +{ + "currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1", + "currentNumber": "0x01", + "currentTimestamp": "0x54c99069", + "currentGasLimit": "0x2fefd8" +} diff --git a/test/integration/t8n/cancun_create_tx/txs.json b/test/integration/t8n/cancun_create_tx/txs.json new file mode 100644 index 00000000..e8fc3c68 --- /dev/null +++ b/test/integration/t8n/cancun_create_tx/txs.json @@ -0,0 +1,44 @@ +[ + { + "to": null, + "input": "0x60015ff3", + "gas": "0x186a0", + "nonce": "0x0", + "value": "0x0", + "gasPrice": "0x32", + "chainId": "0x1", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "v": "0x1b", + "r": "0x468a915f087692bb9be503831a3dfef2cf9c8dee26deb40ff2ec99e8d22665ae", + "s": "0x5cedae0810c3851ecd1004bfdbfe6ddc7753c2d665993bb01ce75af7857b13dc" + }, + { + "input": "0x00", + "gas": "0x3d0900", + "nonce": "0x0", + "value": "0x186a0", + "v": "0x0", + "r": "0xfc12b67159a3567f8bdbc49e0be369a2e20e09d57a51c41310543a4128409464", + "s": "0x2de0cfe5495c4f58ff60645ceda0afd67a4c90a70bc89fe207269435b35e5b67", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "chainId": "0x1", + "type": "0x3", + "maxFeePerGas": "0x12a05f200", + "maxPriorityFeePerGas": "0x2", + "accessList": [ + { + "address": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ] + } + ], + "maxFeePerBlobGas": "0xa", + "blobVersionedHashes": [ + "0x01a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + ], + "hash": "0xf564e770e59f0e74ea8d992ba407cabb2847ac66019911106e2ed97b330e26a8", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } +] diff --git a/test/internal_benchmarks/.clang-tidy b/test/internal_benchmarks/.clang-tidy new file mode 100644 index 00000000..a52342de --- /dev/null +++ b/test/internal_benchmarks/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: > + -clang-analyzer-unix.Malloc diff --git a/test/internal_benchmarks/CMakeLists.txt b/test/internal_benchmarks/CMakeLists.txt index 2a5a4902..4b5228d6 100644 --- a/test/internal_benchmarks/CMakeLists.txt +++ b/test/internal_benchmarks/CMakeLists.txt @@ -4,8 +4,9 @@ add_executable( evmone-bench-internal + evmmax_bench.cpp find_jumpdest_bench.cpp memory_allocation.cpp ) -target_link_libraries(evmone-bench-internal PRIVATE benchmark::benchmark) +target_link_libraries(evmone-bench-internal PRIVATE evmone::evmmax benchmark::benchmark) diff --git a/test/internal_benchmarks/evmmax_bench.cpp b/test/internal_benchmarks/evmmax_bench.cpp new file mode 100644 index 00000000..7f8f7b31 --- /dev/null +++ b/test/internal_benchmarks/evmmax_bench.cpp @@ -0,0 +1,63 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +using namespace intx; + +namespace +{ +constexpr auto bn254 = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256; +constexpr auto secp256k1 = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_u256; + +template +void evmmax_add(benchmark::State& state) +{ + const evmmax::ModArith m{Mod}; + auto a = Mod / 2; + auto b = Mod / 3; + + while (state.KeepRunningBatch(2)) + { + a = m.add(a, b); + b = m.add(b, a); + } +} + +template +void evmmax_sub(benchmark::State& state) +{ + const evmmax::ModArith m{Mod}; + auto a = Mod / 2; + auto b = Mod / 3; + + while (state.KeepRunningBatch(2)) + { + a = m.sub(a, b); + b = m.sub(b, a); + } +} + +template +void evmmax_mul(benchmark::State& state) +{ + const evmmax::ModArith m{Mod}; + auto a = m.to_mont(Mod / 2); + auto b = m.to_mont(Mod / 3); + + while (state.KeepRunningBatch(2)) + { + a = m.mul(a, b); + b = m.mul(b, a); + } +} +} // namespace + +BENCHMARK_TEMPLATE(evmmax_add, uint256, bn254); +BENCHMARK_TEMPLATE(evmmax_add, uint256, secp256k1); +BENCHMARK_TEMPLATE(evmmax_sub, uint256, bn254); +BENCHMARK_TEMPLATE(evmmax_sub, uint256, secp256k1); +BENCHMARK_TEMPLATE(evmmax_mul, uint256, bn254); +BENCHMARK_TEMPLATE(evmmax_mul, uint256, secp256k1); diff --git a/test/internal_benchmarks/memory_allocation.cpp b/test/internal_benchmarks/memory_allocation.cpp index 46a4d1ec..a4a3894b 100644 --- a/test/internal_benchmarks/memory_allocation.cpp +++ b/test/internal_benchmarks/memory_allocation.cpp @@ -13,14 +13,14 @@ namespace { void malloc_(size_t size) noexcept { - const auto m = std::malloc(size); + auto m = std::malloc(size); benchmark::DoNotOptimize(m); std::free(m); } void calloc_(size_t size) noexcept { - const auto m = std::calloc(1, size); + auto m = std::calloc(1, size); benchmark::DoNotOptimize(m); std::free(m); } @@ -28,7 +28,7 @@ void calloc_(size_t size) noexcept void os_specific(size_t size) noexcept { #if defined(__unix__) || defined(__APPLE__) - const auto m = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0); + auto m = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0); benchmark::DoNotOptimize(m); munmap(m, size); #else diff --git a/test/precompiles_bench/CMakeLists.txt b/test/precompiles_bench/CMakeLists.txt new file mode 100644 index 00000000..28237cf4 --- /dev/null +++ b/test/precompiles_bench/CMakeLists.txt @@ -0,0 +1,15 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2024 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +add_executable(evmone-precompiles-bench) +target_compile_features(evmone-precompiles-bench PRIVATE cxx_std_20) +target_include_directories(evmone-precompiles-bench PRIVATE ..) +target_link_libraries(evmone-precompiles-bench PRIVATE evmone::state benchmark::benchmark) +target_sources( + evmone-precompiles-bench PRIVATE + precompiles_bench.cpp +) + +add_test(NAME evmone/evmone-precompiles-bench COMMAND evmone-precompiles-bench --benchmark_min_time=1x) +set_tests_properties(evmone/evmone-precompiles-bench PROPERTIES FAIL_REGULAR_EXPRESSION "ERROR") diff --git a/test/precompiles_bench/precompiles_bench.cpp b/test/precompiles_bench/precompiles_bench.cpp new file mode 100644 index 00000000..0209feb3 --- /dev/null +++ b/test/precompiles_bench/precompiles_bench.cpp @@ -0,0 +1,223 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/utils.hpp" +#include +#include +#include +#include +#include + +#ifdef EVMONE_PRECOMPILES_SILKPRE +#include +#endif + +namespace +{ +using namespace evmone::state; +using namespace evmone::test; + +using ExecuteFn = ExecutionResult( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; + +template +constexpr auto analyze = 0; +template <> +constexpr auto analyze = identity_analyze; +template <> +constexpr auto analyze = ecrecover_analyze; +template <> +constexpr auto analyze = ecadd_analyze; +template <> +constexpr auto analyze = ecmul_analyze; +template <> +[[maybe_unused]] constexpr auto analyze = ecpairing_analyze; +template <> +constexpr auto analyze = point_evaluation_analyze; + +template +const inline std::array inputs{0}; + +template <> +const inline std::array inputs{ + bytes(4096, 0), + bytes(4096, 1), +}; + +template <> +const inline std::array inputs{ + "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" + "000000000000000000000000000000000000000000000000000000000000001c" + "73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f" + "eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"_hex, + + // Inputs from https://etherscan.io/block/19570693 + "e6a825e35ed9be084a71892d40a03353bcc64daf121816aebb219e3a1adadee3000000000000000000000000000000000000000000000000000000000000001c16799fd127010cc658053f838ba1dbcf49274fe97d14da1c08456c917d303ad4495b97615ae683e5677714e1aa5470d2d5e95f50781cee24d12e5972b853a4be"_hex, + "e37c09431f6725ecb8de43e77fa48a598d6dcaee19dbdd0782a56bb5d8d76f1b000000000000000000000000000000000000000000000000000000000000001cd1507bb892a41f02648e61ab306c7acc7efc50669bab3bbb179e4f21386cab5a72c3f4b47f12b7a42dec6e220792b8b7f7c2aefa9e57869d165be54061d38d37"_hex, + "06cfd18cbba471f873110ce951f8709243ad4ee3963c0f37ca748fd8f4cb277d000000000000000000000000000000000000000000000000000000000000001c5e4a4eafc75a38761f986807a7c3c3ce3dc6d8d1b54e77e0d8f61550cb145f5609d82baa98f5affc6c1fa92da7df6bfd957c23dbaa80c43069ddd31de4b06b50"_hex, + "ad11ccc6f37519a248f72617eb6895db5227816ecc02ffb04d7f3ead7c68c9c5000000000000000000000000000000000000000000000000000000000000001cbec7fc01246483c331848dca2435ef566c405f49c80773ed1e37c7d8e5f9763c014f06796c5f2eb28799a7d1b933e420e0a8179a7f041f7b177bb749887e3fdb"_hex, + "845f0b1cab4c6780c61f88cd8018646af7d92f513b8478290ad607ec2c81699b000000000000000000000000000000000000000000000000000000000000001c190b2d1d6152613f92fa9406825206ea21290e4cf61e5729098d35fdf5779d4d4e008b79b927f35879c4a05444113857b0073bf336dfa0ed647809599e6f682d"_hex, + "09b985f366a070978c1871df5d78cf890c8a51417c5a4aeaf6019a25bc6478be000000000000000000000000000000000000000000000000000000000000001bb6d464798c025aacffd1a56e0643a1ac29f6ed62f282c2e5b13bc0bbec08647c299cfa2e7c295fa7387856eedb34a94a30728fe96f6caf3c5e02c6ce0467ec3f"_hex, + "9d160608ad1a2d3c0f9ee91b566a0f63cdd6297b255b7860485dc80915b6edd1000000000000000000000000000000000000000000000000000000000000001cab06c8d09cc508cd173b7c1943fe76b307dfd1472eac56455ad820a17b7748f87874cb1337d734eb8497f7c5b2ae78289a8806f636382140872ba414ba7b2cef"_hex, + "c866f7d081b5ed51c07478c05950c6d49b57f9dc7e9517f2a49235dffad87ff9000000000000000000000000000000000000000000000000000000000000001b8ac1b5ea65ca74923e5d55a36649775fd4a6383a43625ebe72e80cffab2e73cc71fe84fa6b92785ccf0cb47703f6978d02199027c893e2e578d4a3b756f8a60b"_hex, + "c866f7d081b5ed51c07478c05950c6d49b57f9dc7e9517f2a49235dffad87ff9000000000000000000000000000000000000000000000000000000000000001cf584733b44d6a4997ffb9cd2c55ad194fa105f99fb9a02265c4b02a1ab987ea93c83271a5023cda2d536fcb57676a4029c62fab694d6defd997939d0eb738ca8"_hex, + +}; + +template <> +const inline std::array inputs{ + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2" + "16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba" + "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286" + "0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4"_hex, + + // Inputs from + // https://etherscan.io/tx/0xf1d290f8a30e84953e40f32b442f4af4d947ddc88cb9e66f1e551f82d8479e2d + "2aee4c4a210fe1d6dd0c58e82fa8a8ebb8d6ccd57925d6c719240e7a61aff514232667094a59014c57bf0ad6a220bf66ca1e6ab8b4135fa932bffaba7dc9ac212a2652ced6e8fbaa735ba18f3851e8ef8ece83b2c7f685516eb1eb66860c82eb02840447bfa6500e7cb5532a37b89dfea32159a2769b1ab23d56a95ccfa76174"_hex, + "0bd35c81c7113a12eabec37f18750aa99294710f22e57da632212a063f59d27a03cb8a97e16ee905fcb2e8c5a9ae9b97b6a3c20d76ebc46b7949673e1c8f40b507524717221895c6f8c667f0b0f9bba8808c1f5e2b19860619921b5ccd70e4e5032a0a201c3e477d68cddf5e26d95ba03fb6dc7cac53e17e2e8fa39267507e48"_hex, + "0c359809f64010752fe9cde56bee5c6e98d3b362dad9c6e27983a289d7fdec6a10980910c6c33e2b3558464b76f7ce0626f951382ec370c9d92d2828d856731c1d37ba3d0f2cea2251f41a83abf667100a32c090fb5067d42b447f4559398aef07cf5d8560237e4dd19da27b3b49e5e669823375654845561f2108ef4103a574"_hex, + "1af3c46848da0a5fbeae786b72e49f604ccd2c119d17cd46e516f6534b6075a906c3525efa7be813d47fd339faf5ff0e99adcc71c56a545681820441a3f4a1b922754034f74195e95a897f3570e0fda11e21a39081683528aab029bbdd6c5e230e0ea7e78b3ffaf0d45b3e6169515109292541acf2db63deecd198231f7819b2"_hex, + "2d518024c07730b00e5f0252e479062b235759fca26e0460a40cfa4816be56af13a98be139f51ea9c6f1a93877056a7efcf89310f33022922c71263791fc107d16f6c6cd86354c8b06edca713969b8c30c7a93f1215e2c99bd28bc59f8b8186b230807cb63532426c0c2d1e13b0c8bef1da721eabc333888d4c6d6ff1a2e513e"_hex, + "135b515087f247e764f5ee5af75aa52568e34a5738594175bddd43b6f332beea0d70aa546b5f89b5a626f2c97a616f83e0f29722678afed431f3a4abd589c0b42be33cd0bd8ba0e523bd7dd03706154a9709162024a776ee39cacfbcdcf264b523d77a67e344cde7785449d3350ec89e4fd5eb8aad7178f38d824d6113c594c0"_hex, + "02548a1e93c0c6f79894c37384b9aa26770f31a675920542b5974b400c88f5dc20b5de975ed6c5d749d3555e490b7f8d2d8465b0f322a2b6a9e0679fde711cc80091961eb0f7f63b9357cfed46c61eddbc9a88fc58958461d911e1c5cb9750940506e465f95fde47460eb8fdd060e8ae89c06f665264657266c8543d7de4d3df"_hex, + "1b1c30a53bce3f8304f27e460305daa9ee811f9de9ab7aa7f1e69a260d64c0c52e9add32cc1f0266381fe46712fac4f2052f0348d2ff202085540111fd2cd1dd209227f4a682645e784055c717d3f1dfa203f2b26096ac700cfc55082519a999064db2c23bdffe9356ba02520baef6fb898333c5c61cdcf7efc2212ed7cb070a"_hex, + "15fe6f9e26be9c6851f4dceb593c3c3eddccf67abd05e3cba7e816c184c1650a262e4d9cee7dc9c35dcef7ef71d754179d2d2c8a9870b22201a27fed4c137d9b24ce9cd464b722e82eb266cce2be72a00e9f72eb9ea1936e9094b1ba6848a3792ff2346d6546026fe1be4f94416008247512cae8b110c2e06b58db4cb4355117"_hex, +}; + +template <> +const inline std::array inputs{ + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2" + "16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba" + "183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3"_hex, + + // Inputs from + // https://etherscan.io/tx/0xc51e63b391ffddeddf6a797dec9329d2319cff4358989a0e8c41d58968ed8c3d + "028f9d01bc0cb29ed29374f2e1317da679c7aafdec345db14fa3cc7b7aef3bfe1e7f063cfe3939fe829c6305471a7ecee3a1d414fd1564da6a2435fe7a40dd1f102e3dc989d704b6e15d937f543b5f434326f676ba42dc38cab2a6b531e63c86"_hex, + "27c865075b86f234c03f92aca6cdea284c5f74db3631795351602c32ffe35e052ca0622cb684b6d03ad8ce2b33e4b607be11dacdd231d3ab068365782988dfe5102e3dc989d704b6e15d937f543b5f434326f676ba42dc38cab2a6b531e63c86"_hex, + "115ff91c1ff2ec322468e4b55631506f15e0c911c8cbec4fd60e72245836f44b0db5de38601f262bded24fc593c1d15412eec6c3f7fdd1c691befc70fd89c9d916a872d9b0faa63cfacef41a7b0968954386bf94a3b86794a781b4aaa0d296d9"_hex, + "00b8b470c37deeeab63eed4d8374c4bf000a0be91319a4bb144a87a80732bda904ea9ca099d607fcb70678ac3243175c455abe00ac40a2a2c0cd7ee67b2d946315816eec3322d435e2924f2e87c4a68aa13b79aab0fde90a91bbfb736459f896"_hex, + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020fc9202d44e2bf33f6b4a67e111504852a3a7bf361194f69afa319f8423d703f"_hex, + "0e4fdac80cf8f87fe27488e0a398e94aaafb93f41d3623c66e10dc306f9ff3a60135a67a4dfb05a35b766b3bbca81b3afe06e823859d52c29f7203615b565c5b17d6661525677e78137f0e34bdbbeec136c1d64642493e6e2825f4d8970e98a2"_hex, + "00c8a00b8ce94341d4ebf9abe08b5c87ea4d84dc04138a24ceefe0b9c78c4d95067172bf6abf504bbfd5652d643725e79306075513bf5161c3d64b9465964598286e7ccbf31089e0df224914b92a9a0792e323b1f55684c1aa9f7646a62fb3f2"_hex, + "2d56bbb88255cf631060fa06f154fde7bae56510d6caaec5a0bbc22f252d7aca21e9f7e4584b24a4f105b81de76887052deed89fdce114e161d7e7178213ca0b1d102fb0fde27c3d513d530d25a84ec81d410f89c54c3d66ba4689940ce91dea"_hex, + "30543adffffd27f2c512df127e6bbf0463986e439ba55bbd668ca5ae649c3de71525e19d26d342eaa4201d3311cbea20aea70c6293823d58f69bc154a672dd7a18089d4e577b2312bc6b0fdf414b55a27f85eda857c40ccbf6aee4eda308b6ee"_hex, +}; + +template <> +const inline std::array inputs{ + "07ef2650307493a16aca9e26a19d6743fff6625c3d8e54d72066927275b5f1eb0e1c313e3efebda72ff799f0290c8da3ddde69186bd55847394e2aad3c0116410055091d6ff0c5409deb107db7771636f49a4f35b8fd67b521529cf750bfd30d25bbf84313e421938a91a38cc2a6ec95231804b8bf4beee4fe918dcff2c62cfb2e7ef6b31b4bed556d3ae77b6bbc156b145d1a8c3589c3d62e3fa3aecb968bb5260c78f7e4f3da71284d1d94afb6f4baed9a897e3713952cd4f94870a43d8ea8"_hex + "2dbfc3ec62a3eee5a3b4b464bcf1f8527bbca12adea0f1f12033cd4f61b0e09119e55bd0b72c126da18665039556776642ff82e2f347f24fcea2475f4db087df1ae724ab134e5a7c6bd8a116fa5505b259522c0f164a5e8126e3ec7d34465f6e009f1bcdc853f8e3531756bb625b0d1dc014f4ab57c3f79f4f4e2e7ef7e0ead623a8ca5760457e726365b92fd0ceb486665797cd68c35dcffd8e4ae8066691e913ec7182c9fd68331a10f8be0fe885d730de5c7f89aa7d0b7bafaa009bbc9e3e"_hex + "275d967e24153c39567c2275b998a744042c17fd3c8060df5e32dede307fe6891cafe268593a1b223feb7b3a21f3619ff265e24150adadf917c121b196fce1e1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"_hex + "00f5b874a25a5045aa662c0a73309f53fd80cf68c7d6016b330fea535b3b96e42b454797d9c29e4262de0219926fbee1740553f8282c8c94b3f730d805228d5a2f0c63d0c53b3dfbca27b6b43ae7fbf55a38d78a21470996485b03128accc20800556502356e37ed150db2e36531b0f275fd6835c0fc1945922e270b48c48a8602644c27b5dbd793592a70b735e22c798a5e309fa17a992a7dc2a050e01b298f194776b6a53439d7336f389d2a8f6651e40885f5ca2538b0dc9cb534fb23f7fa"_hex, + + "26466d52f79fd83452765af06723b8261d84292750c79260282c8fbebd32beaf1651abcd90107f0c8f610aac5ad733b6c5e1f726f4de82fdb9320446ec24ed76198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d214a3eaef9e104107226efdf0daa209bbe498dc11dd69a532b84d7d96e519f7e2a5ca5391aef6020d334577a46a7b95b650b048352ee1ffa0e780c4a4b6fe91b21c1193ec74d74e40a1cfa846c34eb49a80a85d000fd4a32910d7e683de704742cc717d69a0a0144d55251b862eb0eb8a208e511ac189673f9774f29d20b5ecd0f1e11d24390d4337b9183c1b5168c086f1bdcc1ab340a3685a7d77a0e7e733608fdd631bb135b5287142da57ec7a6e63adde9ab9750804622fce0af05729e34"_hex, + "21f61f88c5a038ceb500aa97ef781055a2d54972cdbf57d517096c1abc61bee200668426d1716b4545c1b0c62fc86c963f0cec21e0cd9581f456e63760c68d89198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa260f33258798435df28cf0d2dfbe5f4f4818c58891bdca78d9072319f01aa1cd032d5dea12336387561dd2dd08fb2a2f7c907a43ab53b971002b0ee7d6cba49c186282957db913abd99f91db59fe69922e95040603ef44c0bd7aa3adeef8f5ac17944351223333f260ddc3b4af45191b856689eda9eab5cbcddbbe570ce860d206d971ff4a7467c3ec596ed6efc674572e32fd6f52b721f97e35b0b3d354675306ecdb9f9567f59ed2eee36e1e1d58797fd13cc97fafc2910f5e8a12f202fa9a"_hex, + "07e7551e3100bff4528b81aac659ca5e4e7170b5e12b8d3e30b9935d3767f48e2ced6ce9c41b8c1acaa3406de772673c367fc348b4b82c7b30da63f10255d3d5198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0b6512359b04db934f686fbcb6c928f354d1a81ae2252ca5d4aae3896ed8b563288f9675e1e95abf899b49fd15fd0989aa1b690e6d91c29309f55cd78caeffe1260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c10118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b004fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe422febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55"_hex, + "0dde47c97939443b8ad501c1f9c5f300c8c38b1df716bfcb1710a274950928cb2ef81b076bfe99bf22b96be86ce80533e6888408f8bcc4f80c4a5a4269e745cd0cd0ec776fcd7c8e09c7870d862d38e24a558caf29addc0ddd75b1cf7467b4822e2c6b91e4c6214282f540b93f9a34f4efa6beb19b12ffe3ecc4180ace6d71ed1760fdf5eba14deb33e8388d49ada8b79244bfc6e7955d1eca9034600a5f87572bb0ea13cd218cb83ad685aad55c7044d3dabbff132ba998b8c7b8d33ee31ad001ebed5ab04cf6a52c91c5eeb8115f16d17f65e332972aae330b7f336a12ed5e28cade7e9fe17f6e3b37c95306c0e95601502c5b92c8eaa4b833ca8cb371829c2129813bd7247065ac58eac42c81e874044e199f48c12aa749a9fe6bb6e4bddc1b72b9ab4579283e62445555d5b2921424213d09a776152361c46988b82be8a7111bc8198f932e379b8f9825f01af0f5e5cacbf8bfe274bf674f6eaa6e338e04259f58d438fd6391e158c991e155966218e6a432703a84068a325439657498572d13bbd21d26b18fc8675a11aab05a2db4cc5e67cf6b613e6cca5605b7dbcdc5021fa68be4bf10f345c6d5cfca0d9a7d31d3ed34b6313189e71ebb6eaf78242e198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2ee53e6ae6469ac25f8a9077a70dc7b2de87b177f37bcaa250bfe0bf1a27c0f41a08736142026f8818c788ed8ab7d3b9095a023ec263885af13c3e9f8f0ee74f0106b9acd3190d55559d315663fd7c9328e349ed9e65609ed04965faa585cc431c440555fec15da4b137a22b124d13d089ed9fbb6255759b86abf9de6e1c761802a73357f1acee0de463c4d3f7535fb23b1317bf6f2ebd078cb7a2e06f882284226254d43204a6add64a6ba65c34c0e6dd13a10888686512efe422afa9608bc6"_hex, + "22c1231e502334e41aca24dee7d962bd7deb7bf45beefd24baebdc181662032529793c3f9e578f731da72ff7e77c3e4aa596bc70f2f18efe861e09d80063055708a802562853078a00073b1a56bdce6974d49999ffe8e57b090fcf50470ee3241867b1193c349d4178fd38349e5dcacbf7fa5169d207d9dc359b2e099047dfd0250d4a0c29531021d3108e1a4851c42ead8697fd2d964db2896afd65039a5a59059fdbd52731e25f6f035686022edc867603dc01503bbcf3e44ffd0263666d552d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e214bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19260967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a71739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec8099c1293f4a7d755cc5895151912cfd89a6114493298ad4559295ac2c0219bc801505f18b3779a8cf3b4cbd4b3c0dbd99ab2c6e3e825e6e3e3bd56ff6961f117198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0d39a10a81e9e8cd251f6c18b9d8c7a2b4ba3f9506c8001638b1bd4c663ca82b172e3638278ba81d883a0e486d597b3a50f9d9e026cf8bd5e940bf031f1bbbda2c4e22719c67bf7bddedcc98face8265be63ccdcef8139c221f21326aa48d1bd0700cbe071c394f095926c90427fe8a412216739e7633acb7c02cd719c12ecb92b34f9525b49f109aed50c68544acf38750b4c7334709d328de3973744953fcb0923b1a411590566dc946db78f12e46e568b91150d4b929ff7b4b5aba0fbab7b"_hex, + "2b8246fa78c0cdbc5a9b6e3ae245229769e3727bede3510aa5391e222c1a16782463d5b720d0b501b1cb00a30faa508b220acdde498c03838897bf98f1348457198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa1613ee9c617b7e7858928a236adc00a4200a7695a010e142d00966651587f516262f5022362a6547c3a991c74ad56caea45b80d5796eb0668fcf72983d170af0260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c10118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b004fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe422febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55"_hex, + "000aa497c1b40b6f07989a7cca77c3e53dee08632433a90955d0c82f3a445937191c2f6a70d2cdd33e65907a67ebd0520e8394506e64cc43d805a3fd483e56990c0e8dc0367bcba1ec70160c11c981cd60432322c946a0a141cf7955e445e95506c1bcaeed380b606a7c05b696be5264dcb4da87342e6788c7adc536c121720d0bb73da5d6e75c353e3273c9a4196240d83f255f18af276c1ab2f5bff77f0ebb0f7cb99dc5db1aec72f855fc4fc1c6e9c0620405c8ff1343ff958a36cd2c05b301ebed5ab04cf6a52c91c5eeb8115f16d17f65e332972aae330b7f336a12ed5e28cade7e9fe17f6e3b37c95306c0e95601502c5b92c8eaa4b833ca8cb371829c2129813bd7247065ac58eac42c81e874044e199f48c12aa749a9fe6bb6e4bddc1b72b9ab4579283e62445555d5b2921424213d09a776152361c46988b82be8a7111bc8198f932e379b8f9825f01af0f5e5cacbf8bfe274bf674f6eaa6e338e04259f58d438fd6391e158c991e155966218e6a432703a84068a325439657498570ca8a4e8513506772cd613017c98f5d22bef7e6f0f11b020c69a11c42402ae1f0ea63a9620ea5f23af6814584b4839f4b5bc9bc444d6e3a45fdd314f33b5e47d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0228e296bbd09b1ab50534a35e2572c874ed6bc2d743d964c35d1ee6e07c5e91119dbba8ead4ff7f04b9824016551efaa4eafdd2d599ab029149d80bb28eb2b50106b9acd3190d55559d315663fd7c9328e349ed9e65609ed04965faa585cc431c440555fec15da4b137a22b124d13d089ed9fbb6255759b86abf9de6e1c761802a73357f1acee0de463c4d3f7535fb23b1317bf6f2ebd078cb7a2e06f882284226254d43204a6add64a6ba65c34c0e6dd13a10888686512efe422afa9608bc6"_hex, + "08b36935645d3cf03da1a44e1c500220e192bc25d7c2258b0e0162daa4cb51ce12fa21e6b7c12050ae82728acf6ffc0d223bee2cd2fed72a8e666531ccd1ed7e01ef8a2f7b663d2929b81e5fddded62747fdb102360f83e268eaf61524bb9e6f0870d564fb18fadd334959b16b916f9cac89685972d38b991147913daa2ff4d609cb2d1f22a263b144f56f84b5ca7fc3b0cad00246e0fb824f8f4d364e4501c423758d28bc118dd4899d126d7eeb041bf255cdccd7052a7af68f6ce2911a22ca01ebed5ab04cf6a52c91c5eeb8115f16d17f65e332972aae330b7f336a12ed5e28cade7e9fe17f6e3b37c95306c0e95601502c5b92c8eaa4b833ca8cb371829c2129813bd7247065ac58eac42c81e874044e199f48c12aa749a9fe6bb6e4bddc1b72b9ab4579283e62445555d5b2921424213d09a776152361c46988b82be8a7111bc8198f932e379b8f9825f01af0f5e5cacbf8bfe274bf674f6eaa6e338e04259f58d438fd6391e158c991e155966218e6a432703a84068a3254396574985718895f9bae6485ebc32b6eb32f2606164d70c22eb69295caa770dc4df161c12202fc51b5f6b67577223fcb9443b66a5443f1229380ada014b89412c210851241198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2b451f25ea40ef8a6bbf6685bdeb5c517d892b7df354afc9b115112402fdbcc6110e32080d1871020cacc07adbf2bd1f18335375798e7c65728d81fcad2a3110233f3ddcd7a051aa58d45f1cb43db3e34a73f4e8186f1ec0f683045e23c14fdb2eaa95d1fec9deced40c5be4803c6e81b565eb02efb180d5060981640116af4b2c2460c2348677dc303426ce941fd7370aa9af05e0b6edfd7bcd74615e3a894c0231a91b920126bf0547ac461f04a4d351db35c21d9936a11d53eb53ce4a13d2"_hex, + "1bce8fa5482d9e50dfea4fb1dd94522e9b716f44d9918d6708fac0f0881938632a4338da2f280b176ab6924abb4775b55b508a8e36fff191023fdc5e99ba928b13bb5bd8d9a2b2822aacf155d7b22beebd4fe1910bd0dbb2834f2dc949f65cf60fc09cf79df13a761e90268de37a3595a7e9dc971fcaf7b26b784559036dfbb00c4f7aaa0d95a7cdeb1854ddef2a87b9393b6ce296e3c0ec20af93a0fde37abb2b8903140adc77bc3ed134c34571e16e949eb7d4a827da48859a9614aba82f722d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e214bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19260967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a71739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec829ad8a4a19c8558b7586b3748ef5315ba4520e87bf714177ded2b8b0dee8a03e067cb77535c18066291a1f20a16b7fb8f5d609ad5be42d1598bca446703d74c6198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2d1b4674ee05363271f65f69fb5aa3d24145eb76f76bf49477e7950b8b4a050c18a6763f1cbe6d2334efcd94661dd802125011c7aba93167d8e25320640621882c4e22719c67bf7bddedcc98face8265be63ccdcef8139c221f21326aa48d1bd0700cbe071c394f095926c90427fe8a412216739e7633acb7c02cd719c12ecb92b34f9525b49f109aed50c68544acf38750b4c7334709d328de3973744953fcb0923b1a411590566dc946db78f12e46e568b91150d4b929ff7b4b5aba0fbab7b"_hex, +}; + +template <> +const inline std::array inputs{ + // Inputs taken randomly from Mainnet + // https://etherscan.io/address/0x000000000000000000000000000000000000000a/advanced#internaltx + "012b08a0504a63aac18383db69fe6b52fc833e3d060b87c2726c4140c909d91807dddd3c80995c2bb3012943e2036e77490b1f6ddc58ca39a4fb4f3225ae56ab11dc2c4d89f777f0f5c2a51f45b73ff1538761f9cf23ed74c74472fea625ad8bace1db77e25ceb316d914182e05dd810f112352e1d6ed9e47af28e2f64e22b94c411794359c2273bc10bc0390963fb1a97bb642307bfa4424c66bd90ecc0ecffd5045e492b40304df20346693db7450457e2c72588a6a2b1a16909e2ab1e6284"_hex, + "019cd755316533108b9eade41e35a16442ae76acd5b7d4e8903ecb9d9f48348a00000000000000000000000000000000dd372dcb4e5565861fc29cfb12f4373861e6e2dfca75084191a505f7988db8e82a4a4a09734b6fd7677d590a1cb512768c381fc4957f406ef89996d9dfa1d39b5c8d1368569e56fd61036c537400a3f4515eeb0c4d183142daa2c30423e0c3fa84667445c1669d3a3e3fce8a1144811e4452841399318c21cca9d20c91fb162929c4e96d391b70158bcd4c69b682b272"_hex, + "0187576b6a38dd4ca8ce00e35dd12d1dd91e06ba3bde49d01568103d826d59732fd172adc351401950681fb66f9464410b15437ab00599aede3f90d0d9552bd162920cfc9b91d123f2c24034006fc9b7f5217cfae1022be231b6bd37262fda83ac48e50cbfeeee227daee56a8bff2f96ede7757b6f2598bd40b14d75b04c07a92299443d1eabe857f57fc95b0ca8b121adff55fad542926063245c402008a846c60eeca2e419a3e9e12ddfc0184a606b31d39268d8b580b57ac274501858bdb5"_hex, + "0145e6c573f2f24f95eeddb687df7da5da51ae1d58bc3691ff52c8d667e704db0000000000000000000000000000000076231706fc7f1f5cd05a88ed538b74ab2672abe246a6042b24c709294ea2e85672a6edc7aee8ab556adfd7af889086afa1bf23edb5e0300f4d924350197c497f084af76365a6209c4b93f00b1ffc803fb7847ca51f4fc1df4fdb9298c16d6a1ea038d42f9ffa40e872eb722e095ea19ff4f2e891a2676b93905476bc8186b741f64c7281f7bba1a14e086316bf5e4b67"_hex, + "0129c00365c61f29b376a0ff296052604481bce870816c0b1d8a8643d5903c7e000000000000000000000000000000000ae90ade08feef8d4531b692a6ea98ff07cee3b22e47da139784b96a01c6f2291ecd756cd520c10b4c86db65c7ab754a8c4174fa25da26bb3121232ee8ab7d7bdacf4662971bf9fdc92a374b98dc8ca844ae3950ff5bdd1d956b9169666fcaefb44a2aae8ddcbb7be512604565391bc1d5468488c11dcf27be9f2003a651688d660d82060c225c078192ecb8c25ba802"_hex, + "018490afa27e04a58f6be1ccd8376ad77819954b5c2c950eb88c5e23413e75356056276dbe61c3076bb23c172adb59beb669713669f42036f305d33e39405e4747420b62257ed239139f68d6f70904a3970809e2c159a5d869f2fe3d28f876ef8d6124ae4698b9b10d5333ed73fedde92b76d7009febd0bbd3eef4748bcfbf8080c452fc31461045e03e24d1a4db246a94f75bad459310cec7f3fe8f9798ba615fe3dffba3dd074d4b906e5c0b1a0db916a519f6251ce9b0f327e0628897f271"_hex, + "01ae47a1b7702fdc67641c971a8a6b3296f9273667ac2fc540934aedd8d7a8ef3e3af8f3f90a55415e6e8b50e2b86b1b227de39feef2b98d840f39774220ca492345c4e5ba1ed2c0e414bd22fc9155c786e667b009272c6ff1596329668288b0abd3f3aff02af02fdff85f322067004c0a44ea8626d033f3c95470bc7fdda33a3f2ecf49ea24b94aba043b295b1ba82d8aa2ad2b1b9b2b0556679828f0e64a27988f1d9ebda3b00b9c6dbdee6e50aafdffd092583aeba3d42eb48d0cb09da2cc"_hex, + "015234bf406d81ea1d44ef75f19f9932b426fb8c76ffffe93906d6f7dce0d04900000000000000000000000000000000c3e098d7dad534d2d51cd14da7e73cf442f7483e30344d2efe4d2c7ea3d8b67da3131476983d45d625b0ffabe960395489f402c36d9837eb7c0872a223dd64233e5ba7c6a5dd72de7f8c8406f3f87e5e31df83132ef527aa9dfd3a2f56c98799980fd810ab52f4edd43347a64889d2f9850acbe16d9494478c1a462b69a099a960a98f48971d50b7453e2a03227577b1"_hex, + "01da11f18d57a31a52071af0062552bca6477958994aa068473a7182241ba65c0000000000000000000000000000000061acc5cf049e854931e084b9bc044ecb4bda7c50de18c50ec84b6d13009bb56afa474010fed9a9c861fc76077eb0b327ad2e1b9700e19f435fda3ad6a53d855315cf61ee2cf24d80b9fbf5523ed5ce76a5267617a4b4bd63234d37430ff0274f8024e664b701b13589313f6b315d32122effb0224b223e055c520c3593df101fcb5c51f823d7eb9a2b882e4c9cb46e2b"_hex, + "01ff83fbe4488061b695d9e4632e9ab23c5b9dfb046241fac921659c2e3b6a4e5e88a0c20ef27ae5dea6e83a748115df152b207362c022a43c8ebbfbec73ea2a2b38376a1de4ed9abd35175ac422af7df2c6ddf1503979964b1d217747c108978969cc0fcbd47dba8b1e3cbba16920f3fc694fadbf203b55aa66aaf138a38ace946a7ccddc20f45d796cd60268df8a2487d06c11cdc50247c580c5dd25f6fb486053b73868038cb92c2ae6d429a7bde850177f31c9fde00ca824c8c59cd4ef49"_hex, +}; + +template +void precompile(benchmark::State& state) +{ + int64_t batch_gas_cost = 0; + size_t max_output_size = 0; + for (const auto& input : inputs) + { + const auto r = analyze(input, EVMC_LATEST_STABLE_REVISION); + batch_gas_cost += r.gas_cost; + max_output_size = std::max(max_output_size, r.max_output_size); + } + const auto output = std::make_unique_for_overwrite(max_output_size); + + + int64_t total_gas_used = 0; + while (state.KeepRunningBatch(inputs.size())) + { + for (const auto& input : inputs) + { + const auto [status, _] = Fn(input.data(), input.size(), output.get(), max_output_size); + if (status != EVMC_SUCCESS) [[unlikely]] + { + state.SkipWithError("invalid result"); + return; + } + } + total_gas_used += batch_gas_cost; + } + + using benchmark::Counter; + state.counters["gas_used"] = Counter(static_cast(batch_gas_cost)); + state.counters["gas_rate"] = Counter(static_cast(total_gas_used), Counter::kIsRate); +} + +BENCHMARK_TEMPLATE(precompile, PrecompileId::identity, identity_execute); + +namespace bench_ecrecovery +{ +constexpr auto evmmax_cpp = ecrecover_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecrecover, evmmax_cpp); +#ifdef EVMONE_PRECOMPILES_SILKPRE +constexpr auto libsecp256k1 = silkpre_ecrecover_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecrecover, libsecp256k1); +#endif +} // namespace bench_ecrecovery + +namespace bench_ecadd +{ +constexpr auto evmmax_cpp = ecadd_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecadd, evmmax_cpp); +#ifdef EVMONE_PRECOMPILES_SILKPRE +constexpr auto libff = silkpre_ecadd_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecadd, libff); +#endif +} // namespace bench_ecadd + +namespace bench_ecmul +{ +constexpr auto evmmax_cpp = ecmul_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecmul, evmmax_cpp); +#ifdef EVMONE_PRECOMPILES_SILKPRE +constexpr auto libff = silkpre_ecmul_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecmul, libff); +#endif +} // namespace bench_ecmul + +namespace bench_ecpairing +{ +#ifdef EVMONE_PRECOMPILES_SILKPRE +constexpr auto libff = silkpre_ecpairing_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::ecpairing, libff); +#endif +} // namespace bench_ecpairing + +namespace bench_kzg +{ +constexpr auto evmone_blst = point_evaluation_execute; +BENCHMARK_TEMPLATE(precompile, PrecompileId::point_evaluation, evmone_blst); +} // namespace bench_kzg + +} // namespace + +BENCHMARK_MAIN(); diff --git a/test/state/CMakeLists.txt b/test/state/CMakeLists.txt index 1c152f82..53350650 100644 --- a/test/state/CMakeLists.txt +++ b/test/state/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(evmone-state STATIC) add_library(evmone::state ALIAS evmone-state) -target_link_libraries(evmone-state PUBLIC evmc::evmc_cpp PRIVATE evmone ethash::keccak) +target_link_libraries(evmone-state PUBLIC evmc::evmc_cpp PRIVATE evmone evmone::precompiles ethash::keccak) target_include_directories(evmone-state PRIVATE ${evmone_private_include_dir}) target_sources( evmone-state PRIVATE @@ -12,6 +12,8 @@ target_sources( bloom_filter.hpp bloom_filter.cpp errors.hpp + ethash_difficulty.hpp + ethash_difficulty.cpp hash_utils.hpp hash_utils.cpp host.hpp @@ -22,9 +24,44 @@ target_sources( mpt_hash.cpp precompiles.hpp precompiles.cpp - precompiles_cache.hpp - precompiles_cache.cpp + precompiles_internal.hpp + precompiles_stubs.hpp + precompiles_stubs.cpp rlp.hpp state.hpp state.cpp + system_contracts.hpp + system_contracts.cpp + test_state.hpp + test_state.cpp ) + +option(EVMONE_PRECOMPILES_SILKPRE "Enable precompiles support via silkpre library" OFF) +if(EVMONE_PRECOMPILES_SILKPRE) + include(FetchContent) + FetchContent_Declare( + silkpre + GIT_REPOSITORY https://github.com/torquem-ch/silkpre + GIT_TAG 3322bb898ac9528fc2cf9a8df1e48360420d0c1a + GIT_SHALLOW TRUE + ) + set(BUILD_SHARED_LIBS_ORIG ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + FetchContent_MakeAvailable(silkpre) + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_ORIG}) + + set_target_properties( + silkpre secp256k1 ff + PROPERTIES + COMPILE_OPTIONS -w # Disable warnings. + CXX_CLANG_TIDY "" + ) + + target_link_libraries(evmone-state PRIVATE silkpre) + target_compile_definitions(evmone-state PUBLIC EVMONE_PRECOMPILES_SILKPRE=1) + target_sources( + evmone-state PRIVATE + precompiles_silkpre.hpp + precompiles_silkpre.cpp + ) +endif() diff --git a/test/state/account.hpp b/test/state/account.hpp index 7c115e14..d836525d 100644 --- a/test/state/account.hpp +++ b/test/state/account.hpp @@ -17,10 +17,10 @@ using evmc::bytes32; struct StorageValue { /// The current value. - bytes32 current = {}; + bytes32 current; /// The original value. - bytes32 original = {}; + bytes32 original; evmc_access_status access_status = EVMC_ACCESS_COLD; }; @@ -35,13 +35,15 @@ struct Account uint64_t nonce = 0; /// The account balance. - intx::uint256 balance = {}; + intx::uint256 balance; /// The account storage map. - std::unordered_map storage = {}; + std::unordered_map storage; + + std::unordered_map transient_storage; /// The account code. - bytes code = {}; + bytes code; /// The account has been destructed and should be erased at the end of of a transaction. bool destructed = false; @@ -49,7 +51,13 @@ struct Account /// The account should be erased if it is empty at the end of a transaction. /// This flag means the account has been "touched" as defined in EIP-161 /// or it is a newly created temporary account. - bool erasable = false; + /// + /// Yellow Paper uses term "delete" but it is a keyword in C++ while + /// the term "erase" is used for deleting objects from C++ collections. + bool erase_if_empty = false; + + /// The account has been created in the current transaction. + bool just_created = false; evmc_access_status access_status = EVMC_ACCESS_COLD; diff --git a/test/state/bloom_filter.cpp b/test/state/bloom_filter.cpp index a2049554..6c010fa0 100644 --- a/test/state/bloom_filter.cpp +++ b/test/state/bloom_filter.cpp @@ -54,4 +54,12 @@ BloomFilter compute_bloom_filter(std::span receipts) n return res; } +BloomFilter bloom_filter_from_bytes(const bytes_view& data) noexcept +{ + assert(data.size() == 256); + BloomFilter res; + std::copy(std::begin(data), std::end(data), std::begin(res.bytes)); + return res; +} + } // namespace evmone::state diff --git a/test/state/bloom_filter.hpp b/test/state/bloom_filter.hpp index e07a8ce4..224d4387 100644 --- a/test/state/bloom_filter.hpp +++ b/test/state/bloom_filter.hpp @@ -30,4 +30,7 @@ struct BloomFilter [[nodiscard]] BloomFilter compute_bloom_filter( std::span receipts) noexcept; +/// Loads BloomFilter from bytes_view +BloomFilter bloom_filter_from_bytes(const bytes_view& data) noexcept; + } // namespace evmone::state diff --git a/test/state/errors.hpp b/test/state/errors.hpp index 91f4cd0d..5d9f95d6 100644 --- a/test/state/errors.hpp +++ b/test/state/errors.hpp @@ -16,11 +16,17 @@ enum ErrorCode : int TX_TYPE_NOT_SUPPORTED, INSUFFICIENT_FUNDS, NONCE_HAS_MAX_VALUE, + NONCE_TOO_HIGH, + NONCE_TOO_LOW, TIP_GT_FEE_CAP, FEE_CAP_LESS_THEN_BLOCKS, GAS_LIMIT_REACHED, SENDER_NOT_EOA, INIT_CODE_SIZE_LIMIT_EXCEEDED, + CREATE_BLOB_TX, + EMPTY_BLOB_HASHES_LIST, + INVALID_BLOB_HASH_VERSION, + BLOB_GAS_LIMIT_EXCEEDED, UNKNOWN_ERROR, }; @@ -38,13 +44,17 @@ inline const std::error_category& evmone_category() noexcept case SUCCESS: return ""; case INTRINSIC_GAS_TOO_LOW: - return "intrinsic gas too low:"; + return "intrinsic gas too low"; case TX_TYPE_NOT_SUPPORTED: return "transaction type not supported"; case INSUFFICIENT_FUNDS: return "insufficient funds for gas * price + value"; case NONCE_HAS_MAX_VALUE: return "nonce has max value:"; + case NONCE_TOO_HIGH: + return "nonce too high"; + case NONCE_TOO_LOW: + return "nonce too low"; case TIP_GT_FEE_CAP: return "max priority fee per gas higher than max fee per gas"; case FEE_CAP_LESS_THEN_BLOCKS: @@ -55,6 +65,14 @@ inline const std::error_category& evmone_category() noexcept return "sender not an eoa:"; case INIT_CODE_SIZE_LIMIT_EXCEEDED: return "max initcode size exceeded"; + case CREATE_BLOB_TX: + return "blob transaction must not be a create transaction"; + case EMPTY_BLOB_HASHES_LIST: + return "empty blob hashes list"; + case INVALID_BLOB_HASH_VERSION: + return "invalid blob hash version"; + case BLOB_GAS_LIMIT_EXCEEDED: + return "blob gas limit exceeded"; case UNKNOWN_ERROR: return "Unknown error"; default: diff --git a/test/state/ethash_difficulty.cpp b/test/state/ethash_difficulty.cpp new file mode 100644 index 00000000..73633980 --- /dev/null +++ b/test/state/ethash_difficulty.cpp @@ -0,0 +1,92 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "ethash_difficulty.hpp" +#include +#include + +namespace evmone::state +{ +namespace +{ +int64_t get_bomb_delay(evmc_revision rev) noexcept +{ + switch (rev) + { + default: + return 0; + case EVMC_BYZANTIUM: + return 3'000'000; + case EVMC_CONSTANTINOPLE: + case EVMC_PETERSBURG: + case EVMC_ISTANBUL: + return 5'000'000; + case EVMC_BERLIN: + return 9'000'000; + case EVMC_LONDON: + return 9'700'000; + } +} + +int64_t calculate_difficulty_pre_byzantium(int64_t parent_difficulty, int64_t parent_timestamp, + int64_t current_timestamp, int64_t block_number, evmc_revision rev) +{ + // According to https://eips.ethereum.org/EIPS/eip-2 + const auto period_count = block_number / 100'000; + const auto offset = parent_difficulty / 2048; + + auto diff = parent_difficulty; + + if (rev < EVMC_HOMESTEAD) + diff += offset * (current_timestamp - parent_timestamp < 13 ? 1 : -1); + else + diff += offset * std::max(1 - (current_timestamp - parent_timestamp) / 10, int64_t{-99}); + + if (period_count > 2) + diff += 2 << (block_number / 100'000 - 3); + else if (period_count == 2) + diff += 1; + + return diff; +} + +int64_t calculate_difficulty_since_byzantium(int64_t parent_difficulty, bool parent_has_ommers, + int64_t parent_timestamp, int64_t current_timestamp, int64_t block_number, + evmc_revision rev) noexcept +{ + const auto delay = get_bomb_delay(rev); + const auto fake_block_number = std::max(int64_t{0}, block_number - delay); + const auto p = (fake_block_number / 100'000) - 2; + assert(p < 63); + const auto epsilon = p < 0 ? 0 : int64_t{1} << p; + const auto y = parent_has_ommers ? 2 : 1; + + const auto timestamp_diff = current_timestamp - parent_timestamp; + assert(timestamp_diff > 0); + const auto sigma_2 = std::max(y - timestamp_diff / 9, int64_t{-99}); + const auto x = parent_difficulty / 2048; + return parent_difficulty + x * sigma_2 + epsilon; +} +} // namespace + +int64_t calculate_difficulty(int64_t parent_difficulty, bool parent_has_ommers, + int64_t parent_timestamp, int64_t current_timestamp, int64_t block_number, + evmc_revision rev) noexcept +{ + // The calculation follows Ethereum Yellow Paper section 4.3.4. "Block Header Validity". + static constexpr int64_t MIN_DIFFICULTY = 0x20000; + + if (rev >= EVMC_PARIS) + return 0; // No difficulty after the Merge. + + const auto difficulty = + (rev < EVMC_BYZANTIUM) ? + calculate_difficulty_pre_byzantium( + parent_difficulty, parent_timestamp, current_timestamp, block_number, rev) : + calculate_difficulty_since_byzantium(parent_difficulty, parent_has_ommers, + parent_timestamp, current_timestamp, block_number, rev); + + return std::max(MIN_DIFFICULTY, difficulty); +} +} // namespace evmone::state diff --git a/test/state/ethash_difficulty.hpp b/test/state/ethash_difficulty.hpp new file mode 100644 index 00000000..a271cc2a --- /dev/null +++ b/test/state/ethash_difficulty.hpp @@ -0,0 +1,13 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::state +{ +int64_t calculate_difficulty(int64_t parent_difficulty, bool parent_has_ommers, + int64_t parent_timestamp, int64_t current_timestamp, int64_t block_number, + evmc_revision rev) noexcept; +} // namespace evmone::state diff --git a/test/state/hash_utils.hpp b/test/state/hash_utils.hpp index 6a51b4b9..dd23ebc6 100644 --- a/test/state/hash_utils.hpp +++ b/test/state/hash_utils.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include namespace evmone { @@ -22,13 +22,14 @@ using namespace evmc::literals; /// Better than ethash::hash256 because has some additional handy constructors. using hash256 = bytes32; +/// The hash of the empty RLP list, i.e. keccak256({0xc0}). +static constexpr auto EmptyListHash = + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347_bytes32; + /// Computes Keccak hash out of input bytes (wrapper of ethash::keccak256). inline hash256 keccak256(bytes_view data) noexcept { - const auto eh = ethash::keccak256(data.data(), data.size()); - hash256 h; - std::memcpy(h.bytes, eh.bytes, sizeof(h)); // TODO: Use std::bit_cast. - return h; + return std::bit_cast(ethash::keccak256(data.data(), data.size())); } } // namespace evmone diff --git a/test/state/host.cpp b/test/state/host.cpp index 6d254fa1..cefc7965 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -5,6 +5,7 @@ #include "host.hpp" #include "precompiles.hpp" #include "rlp.hpp" +#include #include namespace evmone::state @@ -64,6 +65,9 @@ evmc_storage_status Host::set_storage( status = EVMC_STORAGE_MODIFIED_RESTORED; // X → Y → X } + // In Berlin this is handled in access_storage(). + if (m_rev < EVMC_BERLIN) + m_state.journal_storage_change(addr, key, storage_slot); storage_slot.current = value; // Update current value. return status; } @@ -74,24 +78,51 @@ uint256be Host::get_balance(const address& addr) const noexcept return (acc != nullptr) ? intx::be::store(acc->balance) : uint256be{}; } +namespace +{ +/// For EXTCODE* instructions if the target is an EOF account, then only return EF00. +/// While we only do this if the caller is legacy, it is not a problem doing this +/// unconditionally, because EOF contracts dot no have EXTCODE* instructions. +bytes_view extcode(bytes_view code) noexcept +{ + return is_eof_container(code) ? code.substr(0, 2) : code; +} + +/// Check if an existing account is the "create collision" +/// as defined in the [EIP-7610](https://eips.ethereum.org/EIPS/eip-7610). +[[nodiscard]] bool is_create_collision(const Account& acc) noexcept +{ + if (acc.nonce != 0 || !acc.code.empty()) + return true; + + // acc.storage may have entries from access list, even if account storage is empty. + // Check for non-zero current values. + if (std::ranges::any_of( + acc.storage, [](auto& e) noexcept { return !is_zero(e.second.current); })) + return true; + + return false; +} +} // namespace + size_t Host::get_code_size(const address& addr) const noexcept { const auto* const acc = m_state.find(addr); - return (acc != nullptr) ? acc->code.size() : 0; + return (acc != nullptr) ? extcode(acc->code).size() : 0; } bytes32 Host::get_code_hash(const address& addr) const noexcept { // TODO: Cache code hash. It will be needed also to compute the MPT hash. const auto* const acc = m_state.find(addr); - return (acc != nullptr && !acc->is_empty()) ? keccak256(acc->code) : bytes32{}; + return (acc != nullptr && !acc->is_empty()) ? keccak256(extcode(acc->code)) : bytes32{}; } size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { const auto* const acc = m_state.find(addr); - const auto code = (acc != nullptr) ? bytes_view{acc->code} : bytes_view{}; + const auto code = (acc != nullptr) ? extcode(acc->code) : bytes_view{}; const auto code_slice = code.substr(std::min(code_offset, code.size())); const auto num_bytes = std::min(buffer_size, code_slice.size()); std::copy_n(code_slice.begin(), num_bytes, buffer_data); @@ -100,68 +131,149 @@ size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_ bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcept { - // Touch beneficiary and transfer all balance to it. - // This may happen multiple times per single account as account's balance - // can be increased with a call following previous selfdestruct. + if (m_state.find(beneficiary) == nullptr) + m_state.journal_create(beneficiary, false); auto& acc = m_state.get(addr); - m_state.touch(beneficiary).balance += acc.balance; - acc.balance = 0; // Zero balance (this can be the beneficiary). + const auto balance = acc.balance; + auto& beneficiary_acc = m_state.touch(beneficiary); - // Mark the destruction if not done already. - return !std::exchange(acc.destructed, true); -} + m_state.journal_balance_change(beneficiary, beneficiary_acc.balance); + m_state.journal_balance_change(addr, balance); -address compute_new_account_address(const address& sender, uint64_t sender_nonce, - const std::optional& salt, bytes_view init_code) noexcept -{ - hash256 addr_base_hash; - if (!salt.has_value()) // CREATE + if (m_rev >= EVMC_CANCUN && !acc.just_created) { - // TODO: Compute CREATE address without using RLP library. - const auto rlp_list = rlp::encode_tuple(sender, sender_nonce); - addr_base_hash = keccak256(rlp_list); + // EIP-6780: + // "SELFDESTRUCT is executed in a transaction that is not the same + // as the contract invoking SELFDESTRUCT was created" + acc.balance = 0; + beneficiary_acc.balance += balance; // Keep balance if acc is the beneficiary. + + // Return "selfdestruct not registered". + // In practice this affects only refunds before Cancun. + return false; } - else // CREATE2 + + // Transfer may happen multiple times per single account as account's balance + // can be increased with a call following previous selfdestruct. + beneficiary_acc.balance += balance; + acc.balance = 0; // Zero balance if acc is the beneficiary. + + // Mark the destruction if not done already. + if (!acc.destructed) { - const auto init_code_hash = keccak256(init_code); - uint8_t buffer[1 + sizeof(sender) + sizeof(*salt) + sizeof(init_code_hash)]; - static_assert(std::size(buffer) == 85); - buffer[0] = 0xff; - std::copy_n(sender.bytes, sizeof(sender), &buffer[1]); - std::copy_n(salt->bytes, sizeof(salt->bytes), &buffer[1 + sizeof(sender)]); - std::copy_n(init_code_hash.bytes, sizeof(init_code_hash), - &buffer[1 + sizeof(sender) + sizeof(salt->bytes)]); - addr_base_hash = keccak256({buffer, std::size(buffer)}); + m_state.journal_destruct(addr); + acc.destructed = true; + return true; } - evmc_address new_addr{}; - std::copy_n(&addr_base_hash.bytes[12], sizeof(new_addr), new_addr.bytes); - return new_addr; + return false; } -std::optional Host::prepare_message(evmc_message msg) +address compute_create_address(const address& sender, uint64_t sender_nonce) noexcept { - auto& sender_acc = m_state.get(msg.sender); - const auto sender_nonce = sender_acc.nonce; + // TODO: Compute CREATE address without using RLP library. + const auto rlp_list = rlp::encode_tuple(sender, sender_nonce); + const auto base_hash = keccak256(rlp_list); + address addr; + std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes); + return addr; +} - // Bump sender nonce. - if (msg.depth == 0 || msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2) - { - if (sender_nonce == Account::NonceMax) - return {}; // Light early exception, cannot happen for depth == 0. - ++sender_acc.nonce; - } +address compute_create2_address( + const address& sender, const bytes32& salt, bytes_view init_code) noexcept +{ + const auto init_code_hash = keccak256(init_code); + uint8_t buffer[1 + sizeof(sender) + sizeof(salt) + sizeof(init_code_hash)]; + static_assert(std::size(buffer) == 85); + auto it = std::begin(buffer); + *it++ = 0xff; + it = std::copy_n(sender.bytes, sizeof(sender), it); + it = std::copy_n(salt.bytes, sizeof(salt), it); + std::copy_n(init_code_hash.bytes, sizeof(init_code_hash), it); + const auto base_hash = keccak256({buffer, std::size(buffer)}); + address addr; + std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes); + return addr; +} - if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2) +std::optional Host::prepare_message(evmc_message msg) noexcept +{ + if (msg.depth == 0 || msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2 || + msg.kind == EVMC_EOFCREATE) { - // Compute and fill create address. - assert(msg.recipient == address{}); - assert(msg.code_address == address{}); - msg.recipient = compute_new_account_address(msg.sender, sender_nonce, - (msg.kind == EVMC_CREATE2) ? std::optional{msg.create2_salt} : std::nullopt, - {msg.input_data, msg.input_size}); - - // By EIP-2929, the access to new created address is never reverted. - access_account(msg.recipient); + auto& sender_acc = m_state.get(msg.sender); + + // EIP-2681 (already checked for depth 0 during transaction validation). + if (sender_acc.nonce == Account::NonceMax) + return {}; // Light early exception. + + if (msg.depth != 0) + { + m_state.journal_bump_nonce(msg.sender); + ++sender_acc.nonce; // Bump sender nonce. + } + + if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2 || msg.kind == EVMC_EOFCREATE) + { + // Compute and set the address of the account being created. + assert(msg.recipient == address{}); + assert(msg.code_address == address{}); + // Nonce was already incremented, but creation calculation needs non-incremented value + assert(sender_acc.nonce != 0); + const auto creation_sender_nonce = sender_acc.nonce - 1; + if (msg.kind == EVMC_CREATE) + msg.recipient = compute_create_address(msg.sender, creation_sender_nonce); + else if (msg.kind == EVMC_CREATE2) + { + msg.recipient = compute_create2_address( + msg.sender, msg.create2_salt, {msg.input_data, msg.input_size}); + } + else + { + assert(msg.kind == EVMC_EOFCREATE); + const bytes_view input = {msg.input_data, msg.input_size}; + + // Indicator of an EOF creation tx - EVMC_EOFCREATE at depth 0. + if (msg.depth == 0) + { + // Assert this is a legacy EOF creation tx. + assert(is_eof_container(input)); + const auto header_or_error = validate_header(m_rev, input); + + const auto* header = std::get_if(&header_or_error); + if (header == nullptr) + return {}; // Light early exception. + + if (!header->has_full_data(msg.input_size)) + return {}; // Light early exception. + + const auto container_size = + static_cast(header->data_offset + header->data_size); + // Follows from the header->can_init condition above. + assert(container_size <= msg.input_size); + + msg.code = msg.input_data; + msg.code_size = container_size; + msg.input_data = msg.input_data + container_size; + msg.input_size = msg.input_size - container_size; + + if (validate_eof(m_rev, ContainerKind::initcode, {msg.code, msg.code_size}) != + EOFValidationError::success) + return {}; // Light early exception. + + msg.recipient = compute_create_address(msg.sender, creation_sender_nonce); + } + // EOFCREATE + else + { + const bytes_view initcontainer{msg.code, msg.code_size}; + msg.recipient = + compute_create2_address(msg.sender, msg.create2_salt, initcontainer); + } + } + + // By EIP-2929, the access to new created address is never reverted. + access_account(msg.recipient); + } } return msg; @@ -169,43 +281,42 @@ std::optional Host::prepare_message(evmc_message msg) evmc::Result Host::create(const evmc_message& msg) noexcept { - assert(msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2); + assert(msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2 || msg.kind == EVMC_EOFCREATE); - // Check collision as defined in pseudo-EIP https://github.com/ethereum/EIPs/issues/684. - // All combinations of conditions (nonce, code, storage) are tested. - // TODO(EVMC): Add specific error codes for creation failures. - if (const auto collision_acc = m_state.find(msg.recipient); - collision_acc != nullptr && (collision_acc->nonce != 0 || !collision_acc->code.empty())) - return evmc::Result{EVMC_FAILURE}; + auto* new_acc = m_state.find(msg.recipient); + const bool new_acc_exists = new_acc != nullptr; + if (!new_acc_exists) + new_acc = &m_state.insert(msg.recipient); + else if (is_create_collision(*new_acc)) + return evmc::Result{EVMC_FAILURE}; // TODO: Add EVMC errors for creation failures. + m_state.journal_create(msg.recipient, new_acc_exists); + + assert(new_acc != nullptr); + assert(new_acc->nonce == 0); - auto& new_acc = m_state.get_or_insert(msg.recipient); - assert(new_acc.nonce == 0); if (m_rev >= EVMC_SPURIOUS_DRAGON) - new_acc.nonce = 1; + new_acc->nonce = 1; // No need to journal: create revert will 0 the nonce. - // Clear the new account storage, but keep the access status (from tx access list). - // This is only needed for tests and cannot happen in real networks. - for (auto& [_, v] : new_acc.storage) [[unlikely]] - v = StorageValue{.access_status = v.access_status}; + new_acc->just_created = true; auto& sender_acc = m_state.get(msg.sender); // TODO: Duplicated account lookup. const auto value = intx::be::load(msg.value); assert(sender_acc.balance >= value && "EVM must guarantee balance"); + m_state.journal_balance_change(msg.sender, sender_acc.balance); + m_state.journal_balance_change(msg.recipient, new_acc->balance); sender_acc.balance -= value; - new_acc.balance += value; // The new account may be prefunded. + new_acc->balance += value; // The new account may be prefunded. auto create_msg = msg; - const bytes_view initcode{msg.input_data, msg.input_size}; - create_msg.input_data = nullptr; - create_msg.input_size = 0; - - if (m_rev >= EVMC_CANCUN && (is_eof_container(initcode) || is_eof_container(sender_acc.code))) + const auto initcode = (msg.kind == EVMC_EOFCREATE ? bytes_view{msg.code, msg.code_size} : + bytes_view{msg.input_data, msg.input_size}); + if (msg.kind != EVMC_EOFCREATE) { - if (validate_eof(m_rev, initcode) != EOFValidationError::success) - return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; + create_msg.input_data = nullptr; + create_msg.input_size = 0; } - auto result = m_vm.execute(*this, m_rev, create_msg, msg.input_data, msg.input_size); + auto result = m_vm.execute(*this, m_rev, create_msg, initcode.data(), initcode.size()); if (result.status_code != EVMC_SUCCESS) { result.create_address = msg.recipient; @@ -216,7 +327,12 @@ evmc::Result Host::create(const evmc_message& msg) noexcept assert(gas_left >= 0); const bytes_view code{result.output_data, result.output_size}; - if (m_rev >= EVMC_SPURIOUS_DRAGON && code.size() > max_code_size) + + // for EOFCREATE successful result is guaranteed to be non-empty + // because container section is not allowed to be empty + assert(msg.kind != EVMC_EOFCREATE || result.status_code != EVMC_SUCCESS || !code.empty()); + + if (m_rev >= EVMC_SPURIOUS_DRAGON && code.size() > MAX_CODE_SIZE) return evmc::Result{EVMC_FAILURE}; // Code deployment cost. @@ -224,48 +340,74 @@ evmc::Result Host::create(const evmc_message& msg) noexcept gas_left -= cost; if (gas_left < 0) { - return (m_rev == EVMC_FRONTIER) ? evmc::Result{EVMC_SUCCESS, result.gas_left} : - evmc::Result{EVMC_FAILURE}; + return (m_rev == EVMC_FRONTIER) ? + evmc::Result{EVMC_SUCCESS, result.gas_left, result.gas_refund, msg.recipient} : + evmc::Result{EVMC_FAILURE}; } - if (m_rev >= EVMC_CANCUN && (is_eof_container(initcode) || is_eof_container(code))) + if (!code.empty() && code[0] == 0xEF) { - if (validate_eof(m_rev, code) != EOFValidationError::success) + if (m_rev >= EVMC_PRAGUE) + { + // Only EOFCREATE/EOF-creation-tx is allowed to deploy code starting with EF. + // It must be valid EOF, which was validated before execution. + if (msg.kind != EVMC_EOFCREATE) + return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; + assert( + validate_eof(m_rev, ContainerKind::runtime, code) == EOFValidationError::success); + } + else if (m_rev >= EVMC_LONDON) + { + // EIP-3541: Reject EF code. return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; + } } - else if (m_rev >= EVMC_LONDON && !code.empty() && code[0] == 0xEF) // Reject EF code. - return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; - // TODO: The new_acc pointer is invalid because of the state revert implementation, - // but this should change if state journal is implemented. - m_state.get(msg.recipient).code = code; + new_acc->code = code; return evmc::Result{result.status_code, gas_left, result.gas_refund, msg.recipient}; } evmc::Result Host::execute_message(const evmc_message& msg) noexcept { - if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2) + if (msg.kind == EVMC_CREATE || msg.kind == EVMC_CREATE2 || msg.kind == EVMC_EOFCREATE) return create(msg); - assert(msg.kind != EVMC_CALL || evmc::address{msg.recipient} == msg.code_address); - auto* const dst_acc = - (msg.kind == EVMC_CALL) ? &m_state.touch(msg.recipient) : m_state.find(msg.code_address); + if (msg.kind == EVMC_CALL) + { + const auto exists = m_state.find(msg.recipient) != nullptr; + if (!exists) + m_state.journal_create(msg.recipient, exists); + } if (msg.kind == EVMC_CALL) { - // Transfer value. - const auto value = intx::be::load(msg.value); - assert(m_state.get(msg.sender).balance >= value); - m_state.get(msg.sender).balance -= value; - dst_acc->balance += value; + if (evmc::is_zero(msg.value)) + m_state.touch(msg.recipient); + else + { + // We skip touching if we send value, because account cannot end up empty. + // It will either have value, or code that transfers this value out, or will be + // selfdestructed anyway. + auto& dst_acc = m_state.get_or_insert(msg.recipient); + + // Transfer value: sender → recipient. + // The sender's balance is already checked therefore the sender account must exist. + const auto value = intx::be::load(msg.value); + assert(m_state.get(msg.sender).balance >= value); + m_state.journal_balance_change(msg.sender, m_state.get(msg.sender).balance); + m_state.journal_balance_change(msg.recipient, dst_acc.balance); + m_state.get(msg.sender).balance -= value; + dst_acc.balance += value; + } } - if (auto precompiled_result = call_precompile(m_rev, msg); precompiled_result.has_value()) - return std::move(*precompiled_result); + if (is_precompile(m_rev, msg.code_address)) + return call_precompile(m_rev, msg); - // Copy of the code. Revert will invalidate the account. - const auto code = dst_acc != nullptr ? dst_acc->code : bytes{}; + // In case msg.recipient == msg.code_address, this is the second lookup of the same address. + const auto* const code_acc = m_state.find(msg.code_address); + const auto code = code_acc != nullptr ? bytes_view{code_acc->code} : bytes_view{}; return m_vm.execute(*this, m_rev, msg, code.data(), code.size()); } @@ -275,8 +417,8 @@ evmc::Result Host::call(const evmc_message& orig_msg) noexcept if (!msg.has_value()) return evmc::Result{EVMC_FAILURE, orig_msg.gas}; // Light exception. - auto state_snapshot = m_state; - auto logs_snapshot = m_logs.size(); + const auto logs_checkpoint = m_logs.size(); + const auto state_checkpoint = m_state.checkpoint(); auto result = execute_message(*msg); @@ -284,11 +426,11 @@ evmc::Result Host::call(const evmc_message& orig_msg) noexcept { static constexpr auto addr_03 = 0x03_address; auto* const acc_03 = m_state.find(addr_03); - const auto is_03_touched = acc_03 != nullptr && acc_03->erasable; + const auto is_03_touched = acc_03 != nullptr && acc_03->erase_if_empty; // Revert. - m_state = std::move(state_snapshot); - m_logs.resize(logs_snapshot); + m_state.rollback(state_checkpoint); + m_logs.resize(logs_checkpoint); // The 0x03 quirk: the touch on this address is never reverted. if (is_03_touched && m_rev >= EVMC_SPURIOUS_DRAGON) @@ -300,7 +442,8 @@ evmc::Result Host::call(const evmc_message& orig_msg) noexcept evmc_tx_context Host::get_tx_context() const noexcept { // TODO: The effective gas price is already computed in transaction validation. - assert(m_tx.max_gas_price >= m_block.base_fee); + // TODO: The effective gas price calculation is broken for system calls (gas price 0). + assert(m_tx.max_gas_price >= m_block.base_fee || m_tx.max_gas_price == 0); const auto priority_gas_price = std::min(m_tx.max_priority_gas_price, m_tx.max_gas_price - m_block.base_fee); const auto effective_gas_price = m_block.base_fee + priority_gas_price; @@ -315,15 +458,22 @@ evmc_tx_context Host::get_tx_context() const noexcept m_block.prev_randao, 0x01_bytes32, // Chain ID is expected to be 1. uint256be{m_block.base_fee}, + intx::be::store(m_block.blob_base_fee), + m_tx.blob_hashes.data(), + m_tx.blob_hashes.size(), }; } bytes32 Host::get_block_hash(int64_t block_number) const noexcept { - (void)block_number; - // TODO: This is not properly implemented, but only single state test requires BLOCKHASH - // and is fine with any value. - return {}; + if (const auto& it = m_block.known_block_hashes.find(block_number); + it != m_block.known_block_hashes.end()) + return it->second; + + // Convention for testing: if the block hash in unknown return the predefined "fake" value. + // https://github.com/ethereum/go-ethereum/blob/v1.12.2/tests/state_test_util.go#L432 + const auto s = std::to_string(block_number); + return keccak256({reinterpret_cast(s.data()), s.size()}); } void Host::emit_log(const address& addr, const uint8_t* data, size_t data_size, @@ -337,18 +487,36 @@ evmc_access_status Host::access_account(const address& addr) noexcept if (m_rev < EVMC_BERLIN) return EVMC_ACCESS_COLD; // Ignore before Berlin. - auto& acc = m_state.get_or_insert(addr, {.erasable = true}); - const auto status = std::exchange(acc.access_status, EVMC_ACCESS_WARM); + auto& acc = m_state.get_or_insert(addr, {.erase_if_empty = true}); - // Overwrite status for precompiled contracts: they are always warm. - if (status == EVMC_ACCESS_COLD && addr >= 0x01_address && addr <= 0x09_address) + if (acc.access_status == EVMC_ACCESS_WARM || is_precompile(m_rev, addr)) return EVMC_ACCESS_WARM; - return status; + m_state.journal_access_account(addr); + acc.access_status = EVMC_ACCESS_WARM; + return EVMC_ACCESS_COLD; } evmc_access_status Host::access_storage(const address& addr, const bytes32& key) noexcept { - return std::exchange(m_state.get(addr).storage[key].access_status, EVMC_ACCESS_WARM); + auto& storage_slot = m_state.get(addr).storage[key]; + m_state.journal_storage_change(addr, key, storage_slot); + return std::exchange(storage_slot.access_status, EVMC_ACCESS_WARM); +} + + +evmc::bytes32 Host::get_transient_storage(const address& addr, const bytes32& key) const noexcept +{ + const auto& acc = m_state.get(addr); + const auto it = acc.transient_storage.find(key); + return it != acc.transient_storage.end() ? it->second : bytes32{}; +} + +void Host::set_transient_storage( + const address& addr, const bytes32& key, const bytes32& value) noexcept +{ + auto& slot = m_state.get(addr).transient_storage[key]; + m_state.journal_transient_storage_change(addr, key, slot); + slot = value; } } // namespace evmone::state diff --git a/test/state/host.hpp b/test/state/host.hpp index 9106ca4c..e5aade8e 100644 --- a/test/state/host.hpp +++ b/test/state/host.hpp @@ -12,21 +12,27 @@ namespace evmone::state { using evmc::uint256be; -inline constexpr size_t max_code_size = 0x6000; -inline constexpr size_t max_initcode_size = 2 * max_code_size; - -/// Computes the address of to-be-created contract. +/// Computes the address of to-be-created contract with the CREATE scheme. /// -/// Computes the new account address for the contract creation context -/// as defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (86). +/// Computes the new account address for the contract creation context of the CREATE instruction +/// or a create transaction. +/// This is defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (88-90), the case for ζ = ∅. /// /// @param sender The address of the message sender. YP: 𝑠. /// @param sender_nonce The sender's nonce before the increase. YP: 𝑛. -/// @param salt The salt for CREATE2. If null, CREATE address is computed. YP: ζ. -/// @param init_code The contract creation init code. Value only affects CREATE2. YP: 𝐢. -/// @return The computed address for CREATE or CREATE2 scheme. -address compute_new_account_address(const address& sender, uint64_t sender_nonce, - const std::optional& salt, bytes_view init_code) noexcept; +/// @return The address computed with the CREATE scheme. +[[nodiscard]] address compute_create_address(const address& sender, uint64_t sender_nonce) noexcept; + +/// Computes the address of to-be-created contract with the CREATE2 / EOFCREATE scheme. +/// +/// Computes the new account address for the contract creation context of the create instruction. +/// +/// @param sender The address of the message sender. +/// @param salt The salt. +/// @param init_code The init_code to hash (initcode or initcontainer). +/// @return The address computed with the scheme. +[[nodiscard]] address compute_create2_address( + const address& sender, const bytes32& salt, bytes_view init_code) noexcept; class Host : public evmc::Host { @@ -56,6 +62,12 @@ class Host : public evmc::Host evmc_storage_status set_storage( const address& addr, const bytes32& key, const bytes32& value) noexcept override; + [[nodiscard]] evmc::bytes32 get_transient_storage( + const address& addr, const bytes32& key) const noexcept override; + + void set_transient_storage( + const address& addr, const bytes32& key, const bytes32& value) noexcept override; + [[nodiscard]] uint256be get_balance(const address& addr) const noexcept override; [[nodiscard]] size_t get_code_size(const address& addr) const noexcept override; @@ -88,7 +100,7 @@ class Host : public evmc::Host /// which may finally be moved to EVM. /// Any state modification is not reverted. /// @return Modified message or std::nullopt in case of EVM exception. - std::optional prepare_message(evmc_message msg); + std::optional prepare_message(evmc_message msg) noexcept; evmc::Result execute_message(const evmc_message& msg) noexcept; }; diff --git a/test/state/mpt.cpp b/test/state/mpt.cpp index 87b487a2..8fad95f2 100644 --- a/test/state/mpt.cpp +++ b/test/state/mpt.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "mpt.hpp" +#include "mpt_hash.hpp" #include "rlp.hpp" #include #include @@ -11,63 +12,63 @@ namespace evmone::state { namespace { +/// The MPT node kind. +enum class Kind : uint8_t +{ + leaf, + ext, + branch +}; + /// The collection of nibbles (4-bit values) representing a path in a MPT. -struct Path +/// +/// TODO(c++26): This is an instance of std::inplace_vector. +class Path { - size_t length = 0; // TODO: Can be converted to uint8_t. - uint8_t nibbles[64]{}; + static constexpr size_t max_size = 64; + + size_t m_size = 0; // TODO: Can be converted to uint8_t. + uint8_t m_nibbles[max_size]{}; +public: Path() = default; - explicit Path(bytes_view key) noexcept : length{2 * key.size()} + /// Constructs a path from a pair of iterators. + Path(const uint8_t* first, const uint8_t* last) noexcept + : m_size(static_cast(last - first)) { - assert(length <= std::size(nibbles)); + assert(m_size <= std::size(m_nibbles)); + std::copy(first, last, m_nibbles); + } + + /// Constructs a path from bytes - each byte will produce 2 nibbles in the path. + explicit Path(bytes_view key) noexcept : m_size{2 * key.size()} + { + assert(m_size <= std::size(m_nibbles) && "a keys must not be longer than 32 bytes"); size_t i = 0; for (const auto b : key) { - // static_cast is only needed in GCC <= 8. - nibbles[i++] = static_cast(b >> 4); - nibbles[i++] = static_cast(b & 0x0f); + m_nibbles[i++] = b >> 4; + m_nibbles[i++] = b & 0x0f; } } - [[nodiscard]] Path tail(size_t pos) const noexcept - { - assert(pos > 0 && pos <= length); // MPT never requests whole path copy (pos == 0). - Path p; - p.length = length - pos; - std::copy_n(&nibbles[pos], p.length, p.nibbles); - return p; - } - - [[nodiscard]] Path head(size_t size) const noexcept - { - assert(size < length); // MPT never requests whole path copy (size == length). - Path p; - p.length = size; - std::copy_n(nibbles, size, p.nibbles); - return p; - } + [[nodiscard]] static constexpr size_t capacity() noexcept { return max_size; } + [[nodiscard]] bool empty() const noexcept { return m_size == 0; } + [[nodiscard]] const uint8_t* begin() const noexcept { return m_nibbles; } + [[nodiscard]] const uint8_t* end() const noexcept { return m_nibbles + m_size; } - [[nodiscard]] bytes encode(bool extended) const + [[nodiscard]] bytes encode(Kind kind) const { - bytes bs; - const auto is_even = length % 2 == 0; - if (is_even) - bs.push_back(0x00); - else - bs.push_back(0x10 | nibbles[0]); - for (size_t i = is_even ? 0 : 1; i < length; ++i) - { - const auto h = nibbles[i++]; - const auto l = nibbles[i]; - assert(h <= 0x0f); - assert(l <= 0x0f); - bs.push_back(static_cast((h << 4) | l)); - } - if (!extended) - bs[0] |= 0x20; - return bs; + assert(kind == Kind::leaf || kind == Kind::ext); + const auto kind_prefix = kind == Kind::leaf ? 0x20 : 0x00; + const auto has_odd_size = m_size % 2 != 0; + const auto nibble_prefix = has_odd_size ? (0x10 | m_nibbles[0]) : 0x00; + + bytes encoded{static_cast(kind_prefix | nibble_prefix)}; + for (auto i = size_t{has_odd_size}; i < m_size; i += 2) + encoded.push_back(static_cast((m_nibbles[i] << 4) | m_nibbles[i + 1])); + return encoded; } }; } // namespace @@ -75,17 +76,10 @@ struct Path /// The MPT Node. /// /// The implementation is based on StackTrie from go-ethereum. -// clang-tidy bug: https://github.com/llvm/llvm-project/issues/50006 +// TODO(clang-tidy-17): bug https://github.com/llvm/llvm-project/issues/50006 // NOLINTNEXTLINE(bugprone-reserved-identifier) class MPTNode { - enum class Kind : uint8_t - { - leaf, - ext, - branch - }; - static constexpr size_t num_children = 16; Kind m_kind = Kind::leaf; @@ -111,8 +105,8 @@ class MPTNode static std::unique_ptr optional_ext( const Path& path, std::unique_ptr child) noexcept { - return (path.length != 0) ? std::make_unique(ext(path, std::move(child))) : - std::move(child); + return (!path.empty()) ? std::make_unique(ext(path, std::move(child))) : + std::move(child); } /// Creates a branch node out of two children and optionally extends it with an extended @@ -128,16 +122,8 @@ class MPTNode br.m_children[idx1] = std::move(child1); br.m_children[idx2] = std::move(child2); - return (path.length != 0) ? ext(path, std::make_unique(std::move(br))) : - std::move(br); - } - - /// Finds the position at witch two paths differ. - static size_t mismatch(const Path& p1, const Path& p2) noexcept - { - assert(p1.length <= p2.length); - return static_cast( - std::mismatch(p1.nibbles, p1.nibbles + p1.length, p2.nibbles).first - p1.nibbles); + return (!path.empty()) ? ext(path, std::make_unique(std::move(br))) : + std::move(br); } public: @@ -151,7 +137,7 @@ class MPTNode void insert(const Path& path, bytes&& value); - [[nodiscard]] hash256 hash() const; + [[nodiscard]] bytes encode() const; }; void MPTNode::insert(const Path& path, bytes&& value) // NOLINT(misc-no-recursion) @@ -160,57 +146,58 @@ void MPTNode::insert(const Path& path, bytes&& value) // NOLINT(misc-no-recursi // in an existing branch node. Otherwise, we need to create new branch node // (possibly with an adjusted extended node) and transform existing nodes around it. + const auto [this_idx, insert_idx] = std::ranges::mismatch(m_path, path); + + // insert_idx is always valid if requirements are fulfilled: + // - if m_path is not shorter than path they must have mismatched nibbles, + // given the requirement of key uniqueness and not being a prefix if existing key, + // - if m_path is shorter and matches the path prefix + // then insert_idx points at path[m_path.size()]. + assert(insert_idx != path.end() && "a key must not be a prefix of another key"); + + const Path common{m_path.begin(), this_idx}; + const Path insert_tail{insert_idx + 1, path.end()}; + switch (m_kind) { case Kind::branch: { - assert(m_path.length == 0); // Branch has no path. - - const auto idx = path.nibbles[0]; - auto& child = m_children[idx]; - if (!child) - child = leaf(path.tail(1), std::move(value)); + assert(m_path.empty()); // Branch has no path. + if (auto& child = m_children[*insert_idx]; child) + child->insert(insert_tail, std::move(value)); else - child->insert(path.tail(1), std::move(value)); + child = leaf(insert_tail, std::move(value)); break; } case Kind::ext: { - assert(m_path.length != 0); // Ext must have non-empty path. - - const auto mismatch_pos = mismatch(m_path, path); - - if (mismatch_pos == m_path.length) // Paths match: go into the child. - return m_children[0]->insert(path.tail(mismatch_pos), std::move(value)); - - const auto orig_idx = m_path.nibbles[mismatch_pos]; - const auto new_idx = path.nibbles[mismatch_pos]; + assert(!m_path.empty()); // Ext must have non-empty path. + if (this_idx == m_path.end()) // Paths match: go into the child. + { + m_children[0]->insert({insert_idx, path.end()}, std::move(value)); + return; + } // The original branch node must be pushed down, possible extended with // the adjusted extended node if the path split point is not directly at the branch node. // Clang Analyzer bug: https://github.com/llvm/llvm-project/issues/47814 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - auto orig_branch = optional_ext(m_path.tail(mismatch_pos + 1), std::move(m_children[0])); - auto new_leaf = leaf(path.tail(mismatch_pos + 1), std::move(value)); - *this = ext_branch(m_path.head(mismatch_pos), orig_idx, std::move(orig_branch), new_idx, - std::move(new_leaf)); + auto this_branch = optional_ext({this_idx + 1, m_path.end()}, std::move(m_children[0])); + auto new_leaf = leaf(insert_tail, std::move(value)); + *this = + ext_branch(common, *this_idx, std::move(this_branch), *insert_idx, std::move(new_leaf)); break; } case Kind::leaf: { - assert(m_path.length != 0); // Leaf must have non-empty path. - - const auto mismatch_pos = mismatch(m_path, path); - assert(mismatch_pos != m_path.length); // Paths must be different. - - const auto orig_idx = m_path.nibbles[mismatch_pos]; - const auto new_idx = path.nibbles[mismatch_pos]; - auto orig_leaf = leaf(m_path.tail(mismatch_pos + 1), std::move(m_value)); - auto new_leaf = leaf(path.tail(mismatch_pos + 1), std::move(value)); - *this = ext_branch(m_path.head(mismatch_pos), orig_idx, std::move(orig_leaf), new_idx, - std::move(new_leaf)); + assert(!m_path.empty()); // Leaf must have non-empty path. + assert(this_idx != m_path.end() && "a key must be unique"); + auto this_leaf = leaf({this_idx + 1, m_path.end()}, std::move(m_value)); + auto new_leaf = leaf(insert_tail, std::move(value)); + *this = + ext_branch(common, *this_idx, std::move(this_leaf), *insert_idx, std::move(new_leaf)); break; } @@ -219,46 +206,49 @@ void MPTNode::insert(const Path& path, bytes&& value) // NOLINT(misc-no-recursi } } -hash256 MPTNode::hash() const // NOLINT(misc-no-recursion) +/// Encodes a node and optionally hashes the encoded bytes +/// if their length exceeds the specified threshold. +static bytes encode_child(const MPTNode& child) noexcept // NOLINT(misc-no-recursion) { + if (auto e = child.encode(); e.size() < 32) + return e; // "short" node + else + return rlp::encode(keccak256(e)); +} + +bytes MPTNode::encode() const // NOLINT(misc-no-recursion) +{ + bytes encoded; switch (m_kind) { case Kind::leaf: { - return keccak256(rlp::encode_tuple(m_path.encode(false), m_value)); + encoded = rlp::encode(m_path.encode(m_kind)) + rlp::encode(m_value); + break; } case Kind::branch: { - assert(m_path.length == 0); - - // Temporary storage for children hashes. - // The `bytes` type could be used instead, but this way dynamic allocation is avoided. - hash256 children_hashes[num_children]; + assert(m_path.empty()); + static constexpr uint8_t empty = 0x80; // encoded empty child - // Views of children hash bytes. - // Additional always empty item is hash list terminator - // (required by the spec, although not needed for uniqueness). - bytes_view children_hash_bytes[num_children + 1]; - - for (size_t i = 0; i < num_children; ++i) + for (const auto& child : m_children) { - if (m_children[i]) - { - children_hashes[i] = m_children[i]->hash(); - children_hash_bytes[i] = children_hashes[i]; - } + if (child) + encoded += encode_child(*child); + else + encoded += empty; } - - return keccak256(rlp::encode(children_hash_bytes)); + encoded += empty; // end indicator + break; } case Kind::ext: { - return keccak256(rlp::encode_tuple(m_path.encode(true), m_children[0]->hash())); + encoded = rlp::encode(m_path.encode(m_kind)) + encode_child(*m_children[0]); + break; } } - assert(false); - return {}; + return rlp::internal::wrap_list(encoded); } @@ -267,17 +257,20 @@ MPT::~MPT() noexcept = default; void MPT::insert(bytes_view key, bytes&& value) { + assert(key.size() <= Path::capacity() / 2); // must fit the path impl. length limit + const Path path{key}; + if (m_root == nullptr) - m_root = MPTNode::leaf(Path{key}, std::move(value)); + m_root = MPTNode::leaf(path, std::move(value)); else - m_root->insert(Path{key}, std::move(value)); + m_root->insert(path, std::move(value)); } [[nodiscard]] hash256 MPT::hash() const { if (m_root == nullptr) - return emptyMPTHash; - return m_root->hash(); + return EMPTY_MPT_HASH; + return keccak256(m_root->encode()); } } // namespace evmone::state diff --git a/test/state/mpt.hpp b/test/state/mpt.hpp index 240c31d7..b464fada 100644 --- a/test/state/mpt.hpp +++ b/test/state/mpt.hpp @@ -8,11 +8,16 @@ namespace evmone::state { -constexpr auto emptyMPTHash = - 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32; - /// Insert-only Merkle Patricia Trie implementation for getting the root hash /// out of (key, value) pairs. +/// +/// Limitations: +/// 1. A key must not be longer than 32 bytes. Protected by debug assert. +/// 2. A key must not be a prefix of another key. Protected by debug assert. +/// This comes from the spec (Yellow Paper Appendix D) - a branch node cannot store a value. +/// 3. A key must be unique. Protected by debug assert. +/// I.e. inserted values cannot be updated by inserting with the same key again. +/// 4. Inserted values cannot be erased. There is no method for this. class MPT { std::unique_ptr m_root; diff --git a/test/state/mpt_hash.cpp b/test/state/mpt_hash.cpp index 316bd4cc..4f7737c9 100644 --- a/test/state/mpt_hash.cpp +++ b/test/state/mpt_hash.cpp @@ -3,31 +3,31 @@ // SPDX-License-Identifier: Apache-2.0 #include "mpt_hash.hpp" -#include "account.hpp" #include "mpt.hpp" #include "rlp.hpp" #include "state.hpp" +#include "test_state.hpp" namespace evmone::state { namespace { -hash256 mpt_hash(const std::unordered_map& storage) +hash256 mpt_hash(const std::map& storage) { MPT trie; for (const auto& [key, value] : storage) { - if (!is_zero(value.current)) // Skip "deleted" values. - trie.insert(keccak256(key), rlp::encode(rlp::trim(value.current))); + if (!is_zero(value)) // Skip "deleted" values. + trie.insert(keccak256(key), rlp::encode(rlp::trim(value))); } return trie.hash(); } } // namespace -hash256 mpt_hash(const std::unordered_map& accounts) +hash256 mpt_hash(const test::TestState& state) { MPT trie; - for (const auto& [addr, acc] : accounts) + for (const auto& [addr, acc] : state) { trie.insert(keccak256(addr), rlp::encode_tuple(acc.nonce, acc.balance, mpt_hash(acc.storage), keccak256(acc.code))); @@ -35,22 +35,18 @@ hash256 mpt_hash(const std::unordered_map& accounts) return trie.hash(); } -hash256 mpt_hash(std::span transactions) +template +hash256 mpt_hash(std::span list) { MPT trie; - for (size_t i = 0; i < transactions.size(); ++i) - trie.insert(rlp::encode(i), rlp::encode(transactions[i])); + for (size_t i = 0; i < list.size(); ++i) + trie.insert(rlp::encode(i), rlp::encode(list[i])); return trie.hash(); } -hash256 mpt_hash(std::span receipts) -{ - MPT trie; - for (size_t i = 0; i < receipts.size(); ++i) - trie.insert(rlp::encode(i), rlp::encode(receipts[i])); - - return trie.hash(); -} +template hash256 mpt_hash(std::span); +template hash256 mpt_hash(std::span); +template hash256 mpt_hash(std::span); } // namespace evmone::state diff --git a/test/state/mpt_hash.hpp b/test/state/mpt_hash.hpp index e0a25a9d..122ae668 100644 --- a/test/state/mpt_hash.hpp +++ b/test/state/mpt_hash.hpp @@ -5,21 +5,33 @@ #include "hash_utils.hpp" #include -#include + +namespace evmone::test +{ +class TestState; +} namespace evmone::state { -struct Account; -struct Transaction; -struct TransactionReceipt; +/// The hash of the empty Merkle Patricia Trie. +/// +/// Specifically, this is the value of keccak256(RLP("")), i.e. keccak256({0x80}). +constexpr auto EMPTY_MPT_HASH = + 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32; /// Computes Merkle Patricia Trie root hash for the given collection of state accounts. -hash256 mpt_hash(const std::unordered_map& accounts); +hash256 mpt_hash(const test::TestState& state); -/// Computes Merkle Patricia Trie root hash for the given collection of transactions. -hash256 mpt_hash(std::span transactions); +/// Computes Merkle Patricia Trie root hash for the given list of structures. +template +hash256 mpt_hash(std::span list); -/// Computes Merkle Patricia Trie root hash for the given collection of transactions receipts. -hash256 mpt_hash(std::span receipts); +/// A helper to automatically convert collections (e.g. vector, array) to span. +template +inline hash256 mpt_hash(const T& list) + requires std::is_convertible_v> +{ + return mpt_hash(std::span{list}); +} } // namespace evmone::state diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 47c9b81b..7df63ea8 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -3,28 +3,35 @@ // SPDX-License-Identifier: Apache-2.0 #include "precompiles.hpp" -#include "precompiles_cache.hpp" +#include "precompiles_internal.hpp" +#include "precompiles_stubs.hpp" +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include -#include #include -#include + +#ifdef EVMONE_PRECOMPILES_SILKPRE +#include "precompiles_silkpre.hpp" +#endif namespace evmone::state { +using evmc::bytes; +using evmc::bytes_view; using namespace evmc::literals; namespace { constexpr auto GasCostMax = std::numeric_limits::max(); -struct PrecompileAnalysis -{ - int64_t gas_cost; - size_t max_output_size; -}; - inline constexpr int64_t num_words(size_t size_in_bytes) noexcept { return static_cast((size_in_bytes + 31) / 32); @@ -36,6 +43,26 @@ inline constexpr int64_t cost_per_input_word(size_t input_size) noexcept return BaseCost + WordCost * num_words(input_size); } +int64_t bls_msm_cost(size_t k, int64_t multiplication_cost) noexcept +{ + assert(k > 0); + + static constexpr int64_t MULTIPLIER = 1000; + static constexpr int16_t DISCOUNT[128] = {1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, + 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, + 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, + 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, + 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, + 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, + 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, + 181, 180, 179, 179, 178, 177, 176, 176, 175, 174}; + + const auto d = DISCOUNT[std::min(k, std::size(DISCOUNT)) - 1]; + return (static_cast(k) * multiplication_cost * d) / MULTIPLIER; +} + +} // namespace + PrecompileAnalysis ecrecover_analyze(bytes_view /*input*/, evmc_revision /*rev*/) noexcept { return {3000, 32}; @@ -123,9 +150,12 @@ PrecompileAnalysis expmod_analyze(bytes_view input, evmc_revision rev) noexcept }; static constexpr auto mult_complexity_eip198 = [](const uint256& x) noexcept { const auto x2 = x * x; - return (x <= 64) ? x2 : - (x <= 1024) ? (x2 >> 2) + 96 * x - 3072 : - (x2 >> 4) + 480 * x - 199680; + if (x <= 64) + return x2; + else if (x <= 1024) + return (x2 >> 2) + 96 * x - 3072; + else + return (x2 >> 4) + 480 * x - 199680; }; const auto max_len = std::max(mod_len, base_len); @@ -138,63 +168,446 @@ PrecompileAnalysis expmod_analyze(bytes_view input, evmc_revision rev) noexcept static_cast(mod_len)}; } +PrecompileAnalysis point_evaluation_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto POINT_EVALUATION_PRECOMPILE_GAS = 50000; + return {POINT_EVALUATION_PRECOMPILE_GAS, 64}; +} + +PrecompileAnalysis bls12_g1add_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_G1ADD_PRECOMPILE_GAS = 500; + return {BLS12_G1ADD_PRECOMPILE_GAS, 128}; +} + +PrecompileAnalysis bls12_g1mul_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_G1MUL_PRECOMPILE_GAS = 12000; + return {BLS12_G1MUL_PRECOMPILE_GAS, 128}; +} + +PrecompileAnalysis bls12_g1msm_analyze(bytes_view input, evmc_revision) noexcept +{ + if (input.empty() || input.size() % 160 != 0) + return {GasCostMax, 0}; + + static constexpr auto BLS12_G1MUL_PRECOMPILE_GAS = 12000; + return {bls_msm_cost(input.size() / 160, BLS12_G1MUL_PRECOMPILE_GAS), 128}; +} + +PrecompileAnalysis bls12_g2add_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_G2ADD_PRECOMPILE_GAS = 800; + return {BLS12_G2ADD_PRECOMPILE_GAS, 256}; +} + +PrecompileAnalysis bls12_g2mul_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_G2MUL_PRECOMPILE_GAS = 45000; + return {BLS12_G2MUL_PRECOMPILE_GAS, 256}; +} + +PrecompileAnalysis bls12_g2msm_analyze(bytes_view input, evmc_revision) noexcept +{ + if (input.empty() || input.size() % 288 != 0) + return {GasCostMax, 0}; + + static constexpr auto BLS12_G2MUL_PRECOMPILE_GAS = 45000; + return {bls_msm_cost(input.size() / 288, BLS12_G2MUL_PRECOMPILE_GAS), 256}; +} + +PrecompileAnalysis bls12_pairing_check_analyze(bytes_view input, evmc_revision) noexcept +{ + static constexpr auto PAIR_SIZE = 384; + + if (input.empty() || input.size() % PAIR_SIZE != 0) + return {GasCostMax, 0}; + + const auto npairs = static_cast(input.size()) / PAIR_SIZE; + + static constexpr auto BLS12_PAIRING_CHECK_BASE_FEE_PRECOMPILE_GAS = 65000; + static constexpr auto BLS12_PAIRING_CHECK_FEE_PRECOMPILE_GAS = 43000; + return {BLS12_PAIRING_CHECK_BASE_FEE_PRECOMPILE_GAS + + BLS12_PAIRING_CHECK_FEE_PRECOMPILE_GAS * npairs, + 32}; +} + +PrecompileAnalysis bls12_map_fp_to_g1_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_MAP_FP_TO_G1_PRECOMPILE_GAS = 5500; + return {BLS12_MAP_FP_TO_G1_PRECOMPILE_GAS, 128}; +} + +PrecompileAnalysis bls12_map_fp2_to_g2_analyze(bytes_view, evmc_revision) noexcept +{ + static constexpr auto BLS12_MAP_FP2_TO_G2_PRECOMPILE_GAS = 75000; + return {BLS12_MAP_FP2_TO_G2_PRECOMPILE_GAS, 256}; +} + +ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 32); + + uint8_t input_buffer[128]{}; + if (input_size != 0) + std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer))); + + ethash::hash256 h{}; + std::memcpy(h.bytes, input_buffer, sizeof(h)); + + const auto v = intx::be::unsafe::load(input_buffer + 32); + if (v != 27 && v != 28) + return {EVMC_SUCCESS, 0}; + const bool parity = v == 28; + + const auto r = intx::be::unsafe::load(input_buffer + 64); + const auto s = intx::be::unsafe::load(input_buffer + 96); + + const auto res = evmmax::secp256k1::ecrecover(h, r, s, parity); + if (res) + { + std::memset(output, 0, 12); + std::memcpy(output + 12, res->bytes, 20); + return {EVMC_SUCCESS, 32}; + } + else + return {EVMC_SUCCESS, 0}; +} + +ExecutionResult sha256_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 32); + crypto::sha256(reinterpret_cast(output), reinterpret_cast(input), + input_size); + return {EVMC_SUCCESS, 32}; +} + +ExecutionResult ripemd160_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 32); + output = std::fill_n(output, 12, std::uint8_t{0}); + crypto::ripemd160(reinterpret_cast(output), + reinterpret_cast(input), input_size); + return {EVMC_SUCCESS, 32}; +} + +ExecutionResult ecadd_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 64); + + uint8_t input_buffer[128]{}; + if (input_size != 0) + std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer))); + + const evmmax::bn254::Point p = {intx::be::unsafe::load(input_buffer), + intx::be::unsafe::load(input_buffer + 32)}; + const evmmax::bn254::Point q = {intx::be::unsafe::load(input_buffer + 64), + intx::be::unsafe::load(input_buffer + 96)}; + + if (evmmax::bn254::validate(p) && evmmax::bn254::validate(q)) + { + const auto res = evmmax::bn254::add(p, q); + intx::be::unsafe::store(output, res.x); + intx::be::unsafe::store(output + 32, res.y); + return {EVMC_SUCCESS, 64}; + } + else + return {EVMC_PRECOMPILE_FAILURE, 0}; +} + +ExecutionResult ecmul_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 64); + + uint8_t input_buffer[96]{}; + if (input_size != 0) + std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer))); + + const evmmax::bn254::Point p = {intx::be::unsafe::load(input_buffer), + intx::be::unsafe::load(input_buffer + 32)}; + const auto c = intx::be::unsafe::load(input_buffer + 64); + + if (evmmax::bn254::validate(p)) + { + const auto res = evmmax::bn254::mul(p, c); + intx::be::unsafe::store(output, res.x); + intx::be::unsafe::store(output + 32, res.y); + return {EVMC_SUCCESS, 64}; + } + else + return {EVMC_PRECOMPILE_FAILURE, 0}; +} + ExecutionResult identity_execute(const uint8_t* input, size_t input_size, uint8_t* output, [[maybe_unused]] size_t output_size) noexcept { - assert(output_size == input_size); + assert(output_size >= input_size); std::copy_n(input, input_size, output); return {EVMC_SUCCESS, input_size}; } +ExecutionResult blake2bf_execute(const uint8_t* input, [[maybe_unused]] size_t input_size, + uint8_t* output, [[maybe_unused]] size_t output_size) noexcept +{ + static_assert(std::endian::native == std::endian::little, + "blake2bf only works correctly on little-endian architectures"); + assert(input_size >= 213); + assert(output_size >= 64); + + const auto rounds = intx::be::unsafe::load(input); + input += sizeof(rounds); + + uint64_t h[8]; + std::memcpy(h, input, sizeof(h)); + input += sizeof(h); + + uint64_t m[16]; + std::memcpy(m, input, sizeof(m)); + input += sizeof(m); + + uint64_t t[2]; + std::memcpy(t, input, sizeof(t)); + input += sizeof(t); + + const auto f = *input; + if (f != 0 && f != 1) [[unlikely]] + return {EVMC_PRECOMPILE_FAILURE, 0}; + + crypto::blake2b_compress(rounds, h, m, t, f != 0); + std::memcpy(output, h, sizeof(h)); + return {EVMC_SUCCESS, sizeof(h)}; +} + +ExecutionResult point_evaluation_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + assert(output_size >= 64); + if (input_size != 192) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + const auto r = crypto::kzg_verify_proof(reinterpret_cast(&input[0]), + reinterpret_cast(&input[32]), + reinterpret_cast(&input[64]), + reinterpret_cast(&input[96]), + reinterpret_cast(&input[96 + 48])); + + if (!r) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + // Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values + // as required by the EIP-4844. + intx::be::unsafe::store(output, crypto::FIELD_ELEMENTS_PER_BLOB); + intx::be::unsafe::store(output + 32, crypto::BLS_MODULUS); + return {EVMC_SUCCESS, 64}; +} + +ExecutionResult bls12_g1add_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 256) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::g1_add(output, &output[64], input, &input[64], &input[128], &input[192])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; +} + +ExecutionResult bls12_g1mul_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 160) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::g1_mul(output, &output[64], input, &input[64], &input[128])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; +} + +ExecutionResult bls12_g1msm_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size % 160 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::g1_msm(output, &output[64], input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; +} + +ExecutionResult bls12_g2add_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 512) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 256); + + if (!crypto::bls::g2_add(output, &output[128], input, &input[128], &input[256], &input[384])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 256}; +} + +ExecutionResult bls12_g2mul_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 288) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 256); + + if (!crypto::bls::g2_mul(output, &output[128], input, &input[128], &input[256])) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 256}; +} + +ExecutionResult bls12_g2msm_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size % 288 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 256); + + if (!crypto::bls::g2_msm(output, &output[128], input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 256}; +} + +ExecutionResult bls12_pairing_check_execute(const uint8_t* input, size_t input_size, + uint8_t* output, [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size % 384 != 0) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 32); + + if (!crypto::bls::pairing_check(output, input, input_size)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 32}; +} + +ExecutionResult bls12_map_fp_to_g1_execute(const uint8_t* input, size_t input_size, uint8_t* output, + [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 64) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 128); + + if (!crypto::bls::map_fp_to_g1(output, &output[64], input)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 128}; +} + +ExecutionResult bls12_map_fp2_to_g2_execute(const uint8_t* input, size_t input_size, + uint8_t* output, [[maybe_unused]] size_t output_size) noexcept +{ + if (input_size != 128) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + assert(output_size == 256); + + if (!crypto::bls::map_fp2_to_g2(output, &output[128], input)) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + return {EVMC_SUCCESS, 256}; +} + +namespace +{ struct PrecompileTraits { decltype(identity_analyze)* analyze = nullptr; decltype(identity_execute)* execute = nullptr; }; -template -ExecutionResult dummy_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept -{ - std::cerr << "Precompile " << static_cast(Id) << " not implemented!\n"; - return ExecutionResult{EVMC_INTERNAL_ERROR, 0}; -} - inline constexpr auto traits = []() noexcept { std::array tbl{{ {}, // undefined for 0 - {ecrecover_analyze, dummy_execute}, - {sha256_analyze, dummy_execute}, - {ripemd160_analyze, dummy_execute}, + {ecrecover_analyze, ecrecover_execute}, + {sha256_analyze, sha256_execute}, + {ripemd160_analyze, ripemd160_execute}, {identity_analyze, identity_execute}, - {expmod_analyze, dummy_execute}, - {ecadd_analyze, dummy_execute}, - {ecmul_analyze, dummy_execute}, - {ecpairing_analyze, dummy_execute}, - {blake2bf_analyze, dummy_execute}, + {expmod_analyze, expmod_stub}, + {ecadd_analyze, ecadd_execute}, + {ecmul_analyze, ecmul_execute}, + {ecpairing_analyze, ecpairing_stub}, + {blake2bf_analyze, blake2bf_execute}, + {point_evaluation_analyze, point_evaluation_execute}, + {bls12_g1add_analyze, bls12_g1add_execute}, + {bls12_g1mul_analyze, bls12_g1mul_execute}, + {bls12_g1msm_analyze, bls12_g1msm_execute}, + {bls12_g2add_analyze, bls12_g2add_execute}, + {bls12_g2mul_analyze, bls12_g2mul_execute}, + {bls12_g2msm_analyze, bls12_g2msm_execute}, + {bls12_pairing_check_analyze, bls12_pairing_check_execute}, + {bls12_map_fp_to_g1_analyze, bls12_map_fp_to_g1_execute}, + {bls12_map_fp2_to_g2_analyze, bls12_map_fp2_to_g2_execute}, }}; +#ifdef EVMONE_PRECOMPILES_SILKPRE + // tbl[static_cast(PrecompileId::ecrecover)].execute = silkpre_ecrecover_execute; + // tbl[static_cast(PrecompileId::sha256)].execute = silkpre_sha256_execute; + // tbl[static_cast(PrecompileId::ripemd160)].execute = silkpre_ripemd160_execute; + tbl[static_cast(PrecompileId::expmod)].execute = silkpre_expmod_execute; + // tbl[static_cast(PrecompileId::ecadd)].execute = silkpre_ecadd_execute; + // tbl[static_cast(PrecompileId::ecmul)].execute = silkpre_ecmul_execute; + tbl[static_cast(PrecompileId::ecpairing)].execute = silkpre_ecpairing_execute; + // tbl[static_cast(PrecompileId::blake2bf)].execute = silkpre_blake2bf_execute; +#endif return tbl; }(); } // namespace -std::optional call_precompile(evmc_revision rev, const evmc_message& msg) noexcept +bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept { // Define compile-time constant, - // TODO: workaround for Clang Analyzer bug https://github.com/llvm/llvm-project/issues/59493. - static constexpr evmc::address address_boundary{NumPrecompiles}; + // TODO(clang18): workaround for Clang Analyzer bug, fixed in clang 18. + // https://github.com/llvm/llvm-project/issues/59493. + static constexpr evmc::address address_boundary{stdx::to_underlying(PrecompileId::latest)}; - if (evmc::is_zero(msg.code_address) || msg.code_address >= address_boundary) - return {}; + if (evmc::is_zero(addr) || addr > address_boundary) + return false; - const auto id = msg.code_address.bytes[19]; - if (rev < EVMC_BYZANTIUM && id > 4) - return {}; + const auto id = addr.bytes[19]; + if (rev < EVMC_BYZANTIUM && id >= stdx::to_underlying(PrecompileId::since_byzantium)) + return false; + + if (rev < EVMC_ISTANBUL && id >= stdx::to_underlying(PrecompileId::since_istanbul)) + return false; + + if (rev < EVMC_CANCUN && id >= stdx::to_underlying(PrecompileId::since_cancun)) + return false; - if (rev < EVMC_ISTANBUL && id > 8) - return {}; + if (rev < EVMC_PRAGUE && id >= stdx::to_underlying(PrecompileId::since_prague)) + return false; - assert(id > 0); + return true; +} + +evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) noexcept +{ assert(msg.gas >= 0); + const auto id = msg.code_address.bytes[19]; const auto [analyze, execute] = traits[id]; const bytes_view input{msg.input_data, msg.input_size}; @@ -203,21 +616,14 @@ std::optional call_precompile(evmc_revision rev, const evmc_messag if (gas_left < 0) return evmc::Result{EVMC_OUT_OF_GAS}; - static Cache cache; - if (auto r = cache.find(static_cast(id), input, gas_left); r.has_value()) - return r; - - uint8_t output_buf[256]; // Big enough to handle all "expmod" tests. - assert(std::size(output_buf) >= max_output_size); - + // Allocate buffer for the precompile's output and pass its ownership to evmc::Result. + // TODO: This can be done more elegantly by providing constructor evmc::Result(std::unique_ptr). + const auto output_data = new (std::nothrow) uint8_t[max_output_size]; const auto [status_code, output_size] = - execute(msg.input_data, msg.input_size, output_buf, max_output_size); - - evmc::Result result{ - status_code, status_code == EVMC_SUCCESS ? gas_left : 0, 0, output_buf, output_size}; - - cache.insert(static_cast(id), input, result); - - return result; + execute(msg.input_data, msg.input_size, output_data, max_output_size); + const evmc_result result{status_code, status_code == EVMC_SUCCESS ? gas_left : 0, 0, + output_data, output_size, + [](const evmc_result* res) noexcept { delete[] res->output_data; }}; + return evmc::Result{result}; } } // namespace evmone::state diff --git a/test/state/precompiles.hpp b/test/state/precompiles.hpp index 79b861d6..520cb7e4 100644 --- a/test/state/precompiles.hpp +++ b/test/state/precompiles.hpp @@ -9,9 +9,7 @@ namespace evmone::state { -/// The total number of known precompiles ids, including 0. -inline constexpr std::size_t NumPrecompiles = 10; - +/// The precompile identifiers and their corresponding addresses. enum class PrecompileId : uint8_t { ecrecover = 0x01, @@ -23,13 +21,30 @@ enum class PrecompileId : uint8_t ecmul = 0x07, ecpairing = 0x08, blake2bf = 0x09, -}; + point_evaluation = 0x0a, + bls12_g1add = 0x0b, + bls12_g1mul = 0x0c, + bls12_g1msm = 0x0d, + bls12_g2add = 0x0e, + bls12_g2mul = 0x0f, + bls12_g2msm = 0x10, + bls12_pairing_check = 0x11, + bls12_map_fp_to_g1 = 0x12, + bls12_map_fp2_to_g2 = 0x13, -struct ExecutionResult -{ - evmc_status_code status_code; - size_t output_size; + since_byzantium = expmod, ///< The first precompile introduced in Byzantium. + since_istanbul = blake2bf, ///< The first precompile introduced in Istanbul. + since_cancun = point_evaluation, ///< The first precompile introduced in Cancun. + since_prague = bls12_g1add, ///< The first precompile introduced in Prague. + latest = bls12_map_fp2_to_g2 ///< The latest introduced precompile (highest address). }; -std::optional call_precompile(evmc_revision rev, const evmc_message& msg) noexcept; +/// The total number of known precompiles ids, including 0. +inline constexpr std::size_t NumPrecompiles = stdx::to_underlying(PrecompileId::latest) + 1; + +/// Checks if the address @p addr is considered a precompiled contract in the revision @p rev. +bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept; + +/// Executes the message to a precompiled contract (msg.code_address must be a precompile). +evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) noexcept; } // namespace evmone::state diff --git a/test/state/precompiles_cache.cpp b/test/state/precompiles_cache.cpp deleted file mode 100644 index 3d5edd1f..00000000 --- a/test/state/precompiles_cache.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2022 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#ifdef _MSC_VER -// Disable warning C4996: 'getenv': This function or variable may be unsafe. -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "precompiles_cache.hpp" -#include -#include -#include - -namespace evmone::state -{ -std::optional Cache::find(PrecompileId id, bytes_view input, int64_t gas_left) const -{ - if (const auto& cache = m_cache.at(stdx::to_underlying(id)); !cache.empty()) - { - const auto input_hash = keccak256(input); - if (const auto it = cache.find(input_hash); it != cache.end()) - { - if (const auto& o = it->second; !o.has_value()) - return evmc::Result{EVMC_PRECOMPILE_FAILURE}; - else - return evmc::Result{EVMC_SUCCESS, gas_left, 0, o->data(), o->size()}; - } - } - return {}; -} - -void Cache::insert(PrecompileId id, bytes_view input, const evmc::Result& result) -{ - if (id == PrecompileId::identity) // Do not cache "identity". - return; - const auto input_hash = keccak256(input); - std::optional cached_output; - if (result.status_code == EVMC_SUCCESS) - cached_output = bytes{result.output_data, result.output_size}; - m_cache.at(stdx::to_underlying(id)).insert({input_hash, std::move(cached_output)}); -} - -Cache::Cache() noexcept -{ - const auto stub_file = std::getenv("EVMONE_PRECOMPILES_STUB"); - if (stub_file == nullptr) - return; - - try - { - const auto j = nlohmann::json::parse(std::ifstream{stub_file}); - for (size_t id = 0; id < j.size(); ++id) - { - auto& cache = m_cache.at(id); - for (const auto& [h_str, j_input] : j[id].items()) - { - auto& e = cache[evmc::from_hex(h_str).value()]; - if (!j_input.is_null()) - e = evmc::from_hex(j_input.get()); - } - } - } - catch (...) - { - std::cerr << "evmone: Loading precompiles stub from '" << stub_file << "' has failed!\n"; - } -} - -Cache::~Cache() noexcept -{ - const auto dump_file = std::getenv("EVMONE_PRECOMPILES_DUMP"); - if (dump_file == nullptr) - return; - - try - { - nlohmann::json j; - for (size_t id = 0; id < std::size(m_cache); ++id) - { - auto& q = j[id]; - for (const auto& [h, o] : m_cache[id]) - { - auto& v = q[evmc::hex(h)]; - if (o) - v = evmc::hex(*o); - } - } - std::ofstream{dump_file} << std::setw(2) << j << '\n'; - } - catch (...) - { - std::cerr << "evmone: Dumping precompiles to '" << dump_file << "' has failed!\n"; - } -} -} // namespace evmone::state diff --git a/test/state/precompiles_cache.hpp b/test/state/precompiles_cache.hpp deleted file mode 100644 index 333976aa..00000000 --- a/test/state/precompiles_cache.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2022 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#include "hash_utils.hpp" -#include "precompiles.hpp" -#include -#include -#include -#include -#include - -namespace evmone::state -{ -using evmc::bytes; -using evmc::bytes_view; - -class Cache -{ - std::array>, NumPrecompiles> m_cache; - -public: - Cache() noexcept; - ~Cache() noexcept; - - /// Lookups the precompiles cache. - /// - /// @param id The precompile ID. - /// @param input The input for precompile execution. - /// @param gas_left The amount of gas left _after_ execution, - /// used for constructing the result for successful execution. - /// @return The cached execution result - /// or std::nullopt if the matching cache entry is not found. - std::optional find(PrecompileId id, bytes_view input, int64_t gas_left) const; - - /// Inserts new precompiles cache entry. - void insert(PrecompileId id, bytes_view input, const evmc::Result& result); -}; -} // namespace evmone::state diff --git a/test/state/precompiles_internal.hpp b/test/state/precompiles_internal.hpp new file mode 100644 index 00000000..b39029f7 --- /dev/null +++ b/test/state/precompiles_internal.hpp @@ -0,0 +1,76 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::state +{ +struct ExecutionResult +{ + evmc_status_code status_code; + size_t output_size; +}; + +struct PrecompileAnalysis +{ + int64_t gas_cost; + size_t max_output_size; +}; + +PrecompileAnalysis ecrecover_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis sha256_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis ripemd160_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis identity_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis expmod_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis ecadd_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis ecmul_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis ecpairing_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis blake2bf_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis point_evaluation_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g1add_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g1mul_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g1msm_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g2add_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g2mul_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_g2msm_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_pairing_check_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_map_fp_to_g1_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; +PrecompileAnalysis bls12_map_fp2_to_g2_analyze(evmc::bytes_view input, evmc_revision rev) noexcept; + +ExecutionResult ecrecover_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult sha256_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult ripemd160_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult identity_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult ecadd_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult ecmul_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult blake2bf_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult point_evaluation_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g1add_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g1mul_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g1msm_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g2add_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g2mul_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_g2msm_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_pairing_check_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_map_fp_to_g1_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +ExecutionResult bls12_map_fp2_to_g2_execute( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept; +} // namespace evmone::state diff --git a/test/state/precompiles_silkpre.cpp b/test/state/precompiles_silkpre.cpp new file mode 100644 index 00000000..8475e1e8 --- /dev/null +++ b/test/state/precompiles_silkpre.cpp @@ -0,0 +1,81 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "precompiles_silkpre.hpp" +#include "precompiles.hpp" +#include +#include +#include + +namespace evmone::state +{ +namespace +{ +ExecutionResult execute(const uint8_t* input, size_t input_size, uint8_t* output_buf, + [[maybe_unused]] size_t max_output_size, PrecompileId id) noexcept +{ + const auto index = stdx::to_underlying(id) - 1; + const auto [output, output_size] = kSilkpreContracts[index].run(input, input_size); + if (output == nullptr) + return {EVMC_PRECOMPILE_FAILURE, 0}; + + // Check if max_output_size computed by analysis match the computed result. + assert(output_size <= max_output_size); + + const auto trimmed_output_size = std::min(output_size, max_output_size); + std::memcpy(output_buf, output, trimmed_output_size); + std::free(output); // Free output allocation (required by silkpre API). + return {EVMC_SUCCESS, trimmed_output_size}; +} +} // namespace + +ExecutionResult silkpre_ecrecover_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::ecrecover); +} + +ExecutionResult silkpre_sha256_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::sha256); +} + +ExecutionResult silkpre_ripemd160_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::ripemd160); +} + +ExecutionResult silkpre_expmod_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::expmod); +} + +ExecutionResult silkpre_ecadd_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::ecadd); +} + +ExecutionResult silkpre_ecmul_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::ecmul); +} + + +ExecutionResult silkpre_ecpairing_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::ecpairing); +} + +ExecutionResult silkpre_blake2bf_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept +{ + return execute(input, input_size, output_buf, max_output_size, PrecompileId::blake2bf); +} +} // namespace evmone::state diff --git a/test/state/precompiles_silkpre.hpp b/test/state/precompiles_silkpre.hpp new file mode 100644 index 00000000..202863cf --- /dev/null +++ b/test/state/precompiles_silkpre.hpp @@ -0,0 +1,33 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "precompiles_internal.hpp" + +namespace evmone::state +{ +ExecutionResult silkpre_ecrecover_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_sha256_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_ripemd160_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_expmod_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_ecadd_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_ecmul_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_ecpairing_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; + +ExecutionResult silkpre_blake2bf_execute( + const uint8_t* input, size_t input_size, uint8_t* output_buf, size_t max_output_size) noexcept; +} // namespace evmone::state diff --git a/test/state/precompiles_stub.json b/test/state/precompiles_stub.json deleted file mode 100644 index 2d15618a..00000000 --- a/test/state/precompiles_stub.json +++ /dev/null @@ -1,696 +0,0 @@ -[ - null, - { - "009d9ee6ae2a8d2d33a6152cbd20b53e8e846228d6f5c3ba6df1c81f16d3f127": "", - "011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce": "", - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "", - "044c8039b0e70b0a95fd48ef3fee93f60176902cedd5e692a3a73f69405300af": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "068218ef30064c84e88ce57ca518f25f82f1d14ae170f243051ba7d42d13f2be": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "0f81fd306d0c0cddd0728a76e6bfb0dfa12891c89994d877f0445483563b380a": "", - "121147ddfb9d189965dfcd9fc6a29d3a1e305fca28524a93dcc02dc1d01caa8d": "0000000000000000000000004c78739de03a70dbcf9b94bc21daf2bf46d44375", - "12ad18e8525e960f8c93c071d7e907981d6a1badf935b80c16e6ad790baf7fc5": "0000000000000000000000000f00d6a30e65104b909aa43d947ef2010e09446a", - "155ebbd01c7ab8c9e897a5907b01160b4b5fba72d9fd8fa7bbbd040ad8e10de3": "", - "15fed0451499512d95f3ec5a41c878b9de55f21878b5b4e190d4667ec709b4cf": "", - "16098be41cfab56cfc4e3d4ecf69407d06a3be90af6ded11696f19fddf23d474": "", - "16d64d4187a874fb2ecabfe23d426940d85ad700c2dbffa2a924a3bb27dbb769": "", - "184125b2e3d1ded2ad3f82a383d9b09bd5bac4ccea4d41092f49523399598aca": "", - "18e0a11d3c25dfdd8c3ebf5a3c431bc88349da31fdab52724d114b321edf959a": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "19617bb47c97a16b82eefd111c3ef2c88f1cb7ad4bbefdd5c52c3cdd910c6651": "", - "1bb6cba805b67c1733b643a25c6f9fee23e62eb39b943c736a781df8a7525680": "", - "1c56a607154e4c8de8c67f33a3106afba5b84fbc7c8c8735b2cb9908a3fc74f5": "", - "1cd9efb2ba7ed02bf88d37210f589f8440ce3365b081b37c07d350cf0fae4371": "", - "1d8453ab2f7716504a4457ebe9831dbf996267e350ad0b2029f654d0dce1e055": "", - "1d9ce567b95236f552a05b29064a31e5bfc7f859e38a2722acc2db99b3d8c8a2": "", - "1da37edc3bb58391e0d9d65acfc452feda09ea79af15ef9d629e927055ad9661": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "1fd6d417f8358520da705dcf9664a19a58dc8c5bc060459f0b30a0de9318a0e5": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "20047d6c3a7cdb146351139ec6ee0f79e7df10a29bf27b9d9764efe10be3d51b": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "20b9a18492383c417bca756db64e24237b56f819771ceea5320f9e862c0b8503": "", - "21694f3ea9a52e859108e909331f609b3ccad56c4c975974094e55316ca124ed": "000000000000000000000000fc4539330fee551b296f9396d01ab7643521d5df", - "23b5d657f8f6a213c0dd0847b57a61477bdebad32c94cfa191a6ed4622f6cdc6": "000000000000000000000000389a57ba1c546578b67167c6571d92e047bd4029", - "248f21a78a10df8e98ae4bc941cac92a8e6e07244b11f43fa70537f81513bf52": "", - "24d0f21724fc3b3bef5d93f5e0d7fc9aab70fe0f1cdc79ccbe81025d1daedc94": "000000000000000000000000ad5a9fc193dcf16041d4e96433ef3a6d82d36b16", - "255ba666481586d8a19c818613b8902832669667049a80f876276e0e8b1b7faf": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "26758654639a74eafce1decd301a0c76f7a57162bad0b763be2765578416f1bf": "", - "276d032750f286c508d060efcddd1b7a9becbfdb64efb5dfcbee057f86722fef": "", - "283ed216d65f248c2522811579b2652e949520ca3f4c0c86af2beb6692dd9c83": "", - "28489601bd9ae32c886d8ae05c9249311ad3d440b6384dd707c4cbda2441c40f": "000000000000000000000000364a9dae48110760306b009bf2297819176be559", - "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": "", - "2af357fc2ab2964b76482ec0fcac3b86f5aca1a8292676023c8b9ec392d821a0": "", - "2ddc60dbbc6777935bc0a435d3a77b75909e18e42a80016f33fb2b8585aac3b3": "", - "2e795758918d9c804da815b3be88b798e63d21d668c624228fbd697bff25ea3b": "", - "2e8aa99ac952c926b1c0bb0a5e3c329c5034064c62ef4110d7e23edea7ee94d6": "000000000000000000000000b4950a7fad428434b11c357fa6d4b4bcd3096a5d", - "2ece9425df06ce26f47bfeb62cd58816af93cbd581da47cf24d392854d101e12": "", - "307e519a0d583c4346c5bbc14c5e5bd3d56cb6096dbfe2c033bed9bd3df24e23": "00000000000000000000000001c954021193a220878900cf5f7db5b3ea4c2b24", - "307f9f77474286909ec7d1de723ce0d1803f25184e288f7f4010c8cab8a9a964": "", - "30e224dbf56e9075c98996690ef5716a82c652d363c75e2895632196016b14b7": "00000000000000000000000019e7e376e7c213b7e7e7e46cc70a5dd086daff2a", - "30e2bfdaad2f3c218a1a8cc54fa1c4e6182b6b7f3bca273390cf587b50b47311": "", - "3184fc86524b0495db56f434fba34e478911db355cbb44815e987625ab42e557": "", - "322ebff356b39a0fdca6dc8a23b37746ce13e6bb5fb284eedc03ab65c7f36d42": "", - "3372afbaf0a4eee019450dba6aa152840875c80c83e433d4b9abda54f414bf7e": "", - "34c2175d62f5203de28e4ad262af278307bab5159024d49ff03b0b5716cf6bde": "", - "3565fe444d17a1d3e7e2c6fcf26239a579b98ec7d6a15a82056159e3c85e066c": "", - "38d1e68159186ade831fbe4872dfa94955c86cced3fd6c4555f5350908efbbac": "", - "3abb6f45dcab55efca9712461264d554ab6facb4c0c0afeb668bf5aa43c41dc8": "", - "3dc5064867fdde3ea414107c203aaf64fa40d079b507f03fb05d5d651bedf6ca": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "3e0cd3adba79d8cdfe018df6b11eeea126842d7c648f55f759a13cc0d8cca632": "0000000000000000000000008324683aaae32ccebdeb758e2777ab2b1ce3d3f1", - "3f0b47fc77754afe57e9f10470ce33d56fb6ef8755868188c43674e45911cc5f": "000000000000000000000000e8e2d3e49d1bb0ddf5beeff311456f251dae9ea9", - "3f14010fd6ad2d379bc84717dcfde1adbd49ab0effd164d6f48687e3b64eec7a": "", - "3f9ae92b3cf5cb96dc4e9dc10e2d2c668577014e6e8a3978cb021a2c9457db1a": "", - "402ca0e8663865c03b0fcae18b2d13b8a1a07b4db2aa174fd4449a666ecd043d": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "40d1059b882052f28a776dffb83ae2000f4975ea9cd103d13c8ebe16bafc8ce4": "", - "41414fecbcd48d24288f4cd69cdc4f11560667f16291c4c642082019a2c613a6": "", - "421f83b9cb3c1673e660435b3392b0ad5cc7d97852d16ea20410bf4bf44c755f": "000000000000000000000000d1d3bc125318dd71176248d9c86f41a842d4bec9", - "43087d053f72c42d4ff586b734a684f275adbda4bff302f6aabbc7f908cbf56e": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "438f6c5a8ed820e3a148e6b3bfaf27a8eb67a5bac583cd51e3f74fa35d2adb36": "", - "44a25c9533b4c9e05472848068a6b5bcb693ce9e222f3f4ac82d2927a82a34ce": "", - "44fc8b2192a7abb4907a620aa0760b455b3aff99736d1a8817d504ea1ec91cbd": "000000000000000000000000295ad34cb312eaf9574511208848caf57b7429e0", - "452ee175b6d3ed54e5d16d3a3218fd899ba99aed67e974d13a75b7442e24efd7": "", - "459c0d89c260a919fbdf013d6707f1794bd332260626b08df4d053c5eedd2aaf": "", - "462c247a22c49aa7de40d030c1d242dfe2fb47003ab3f0b9924677dbff038ddd": "", - "471ccdcb79bddea38175f8cc115b52365f2c864200fbce48e994511bb9c6006f": "", - "48107462b0f7d0d5cc3106b4ddbed581fd1a7182c10fde2e83830c6f13896a1c": "", - "48a6d470f69a7958d16348af5c419d4f155cde98e4fe7e25f0e1e4de2a176d48": "", - "4d8a735acc38ab7f01310ca8e6026ed9f86de88141cd83996db741df5291fc0d": "", - "4f01ca2b90db2ac03064f2958683e809503a6270c67d4219b13b257c5018c178": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "4fef400b93123b210519abd8c81e39e17e4a691a8bdf200e08c3beaa04877033": "", - "5037e1a5e02e081b1b850b130eca7ac17335fdf4c61cc5ff6ae765196fb0d5b3": "", - "50a8aca900c34e14fe43aa0d7f132065c799ac1d8307abec60e78abf3d41472c": "", - "5127452b278723071f64915211d191a7a59f729ab3c9617de94070d86a86c22c": "000000000000000000000000db658a31f5a174be0e3fc0d0ce05dd6a76084910", - "5178143227fe4c2d9ab33dfce3eb6bcc5fc73e407a3fccc4c03be2fdb46cdfe5": "", - "525603e86451df346317e6ccf0c38a1d1a79728e7389c228d69d7a6c136ea083": "000000000000000000000000294091b609877b020b4f5a01357936fc0a877a3f", - "52e07c5ac4c91ad28aec87754a1cd4f14c256516e453b562fff2a8ab56e90e1c": "0000000000000000000000008d682238981c4940830fa6971d25e036d1fb3d27", - "5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a": "", - "5429fdc28e48579bde709c0ca18c55d58f14c9438d5cd1829556be99fd68b97b": "", - "54a8c0ab653c15bfb48b47fd011ba2b9617af01cb45cab344acd57c924d56798": "", - "5562d44b8038c7ec81ac64c10e44ed3b5638aebd3a8cb9d4231533aa9cb3890e": "", - "55cb0ca323c8496a60242fbec05d1a55aa53b3fefbfefa93115fd7f6fe209b4f": "000000000000000000000000e4319f4b631c6d0fcfc84045dbcb676865fe5e13", - "56dd7445e8246202dd28c795ebcb656c70ba9075063cbc3aae6749690fc13afb": "", - "5753138212d78ca85a6b17affd10cca7cffce1fbe25c66b53efc4fadcc45bb91": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "57a8be445205924e80a60daf8a9ac2b83b55eb01eff01bcb1825be87c44dae93": "", - "584f46c60af19681376031579adb04a2416e54ee5505351c2a8435e3766026ea": "", - "59642f809245ca2950deda7acf1d460ac419ef7a8d003ac6bb42f69b01891e5d": "", - "5a1cf9bc06adcfe7397b3edc00612d7ec7dfa0d44afec417e27d3665389cae6b": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "5a22989ecb853baf2104fce8bd10d880da4887a5ec058c170e31a832db4f2a56": "", - "5a657105c493a1213c976c653e929218bb4a516bca307dce5861ec23fffa4e58": "", - "5b6513d4747d845f5045a8daad79201c65d806755f4ffba68b1dcb0c78f5644c": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "5b7d7eba22589dfe1bd7a5417faa2a79838f19c58fe7408d6f66be2327cc996d": "", - "5bf1ea6f086a8edde772752dfa3b9102e6e90497d752287cbaee235d48d1f69e": "", - "5d855fe977abaa48cd5f626a97eb798ae0ffde421de3692c0684845a6c908f47": "", - "5d8af3eccd090407b914616ecb273b9d6c3321c26358e846ccb87a888d338b27": "", - "5efd0f3a104ddc539f268599d2486848f6ca5c071d92023886dbec25407df538": "000000000000000000000000b957b0da344f6a17f0081d63be7345a860e5b7a2", - "660b057b36925d4a0da5bf6588b4c64cff7f27ee34e9c90b052829bf8e2a3168": "", - "6637c8865151f4ac40e4faf213f7d2977eaaa48db59e39f3165abb87e89af8ce": "", - "66506b929ebb30dc155fef68827caf7ae6824e9b0bd7739084032e0289b333af": "", - "67695e93912019e851979ca0f51d809fc95d5c56231031a0a8e56cc735c10f2e": "0000000000000000000000000669457ce81442f235ffc4123662ba14a72b3d68", - "684a72a8364370adc3d8d142106fd330108bc92b6442f7cddda4e80d32d1b83b": "000000000000000000000000628f176bc4c64973abaf9acb6bd8bb8d9b1ae97c", - "6979bb29b5523d4b8ca0ddfd55cff3859487cbf2895662e9ec76e4df9d8aff52": "", - "6a26b4975755a21ffa22fa06d2eaa4c1e6d5ca8834c8719696bc8710f64eee3a": "", - "6bd2dd6bd408cbee33429358bf24fdc64612fbf8b1b4db604518f40ffd34b607": "", - "6e9182355718eec6f927067ac2afc5ecb3263628cf2f2921325fd04d7e3a8c84": "", - "6f9c0300d90788e3eaa2560d9c84298b436854bda9b9a4a9e9e04faa2e5e88c4": "", - "6f9f7d0ce1cd35fb86afc3632aac96fd0b294d1bf711d580a0a32f6437d4a503": "", - "7095093e306a452d41ba776c418b6dd1ffc7c68597b398a913372aa156d31aed": "", - "71db10bdc258e5d3dde75d0d15c5894acd8a0769f0b6935c65357770b1664eaf": "00000000000000000000000099cd51158e59da36ba48b457c02db77c17a6b91a", - "723738cd5b48e795bdb00ebbca91cb4b498909292a6b85555c6eeb7190692f2b": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "73671bbc396a6200adb90f1c8a061271dcf14f76976270cc64d6c97ba3bad94c": "", - "74723bc3efaf59d897623890ae3912b9be3c4c67ccee3ffcf10b36406c722c1b": "", - "74f5c4fd14a98241a85e9423e3fface2601c9dc66f7312cb2c120aba0a3db2aa": "", - "767bfb6ead6760f170718f8074950b9439f9d58e73b64f2554c474039f0e3eb4": "", - "784453c1eb01e806f21450010bb46832f5151aff4f41054783581cc36d411771": "", - "7a679a18af6a41aa0d5b7762b4a3e0401de8d78e66d0bc95b1b0cd144e8d5375": "0000000000000000000000009ca540e3f00347324bd94a94ce8e3a34b97c8244", - "7ac9bd10a3348ddeefe5fd2eea3686e33d5ee977cb0eba91ea90eb208b46a9c1": "00000000000000000000000016fe7fa0cb8a861f855039c2eda9251ca7cc79d0", - "7b4f30428fe6d4352f5dc0eb4f1ce59738fbb1ccf3468278bcd80b641ffdeaed": "0000000000000000000000001b85ac3c9b09de43659c5d04a2d9c75457d9abf4", - "7cdb9d7f02ea58dfeb797ed6b4f7ea68846e4f2b0e30ed1535fc98b60c4ec809": "", - "7e3e86118a8190c9742950d3d561bbfd43a8656d93456b328827efd6ce1589c9": "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "7f2b59c8ac5698b4999e23adaf72c6ed4500fbd0d7734f881c5eb19e7b9ebd75": "", - "827b659bbda2a0bdecce2c91b8b68462545758f3eba2dbefef18e0daf84f5ccd": "", - "8425bc38f66e7882cfe1f974d4ee7f60e5476f4022b013b359ce5b353daf3057": "", - "861aeab295b8278ea72b1865b19d1517c018ff03d9b0a204efbecf7e0dae0804": "", - "86bca244dcf7c90a21f3e6a344ff75070b1182f2ed2d7bdd9b1db823ebffb616": "", - "87d456d16f0132e129b8ce55d8a63de8f9db49e36fc3d4c254860953494b2b77": "", - "8862cb036babe7f70eab2baeb040a97aafc095920878bc170d51c2df8e4d3df4": "0000000000000000000000001387af122c1e31a2dd1dac303b3f20ad83f0ed1b", - "89bb58666f09724bc750b264709704f4c47eaf3c227ed3e7c46e7f141960ae97": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "8c3a47de78197a12495b1b381467f70d10231a44907e73dacc7c548569326dc4": "", - "8ef70542b3dab086ca818bf61eb2c89a31dd266a0f89825cedfff865db68fb2c": "000000000000000000000000dcc53a4a0719101437e8791abf273af5893cb174", - "913b834257c793da6424db222da1ff2f6fd6170ac3094be0405cdcc5552e1a78": "", - "9166c8d72e513a9e3b8389c11481ec071da93e37370fc62bf99c51a7b869a7dd": "", - "91cad3515fc13b619cce04e1638c1cb4c7ad83e080adc65c7d3f00f0164d7e34": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "94374ea151ea3e73d7dd2fb895cb7f0c645bb54bd2d9a388e6209a57af17a19f": "", - "951b260673d281ef5873abcc5c7cca42ce9e4e4a27050f670a4873532efa3b71": "", - "96e2012ae1835f1b97d8521c4558a877b98f5dbd5fbe082f6a7747eaa649fe09": "", - "970267c3e058dd3893130643eb8b30d2a91e553210b887b8f70d5522d22bfeb9": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "998426673b3a3276a06d34611d862b55d977d67df6d1a9869391b775f5128c3b": "", - "99ff0d9125e1fc9531a11262e15aeb2c60509a078c4cc4c64cefdfb06ff68647": "", - "9b09bcaad3f649578d7ef2b6acf584aec1bd37c9dcc3642a9df5202f3f7c80fd": "0000000000000000000000008e5817968f74ffb0255ae41eefa6f89dd0183fa1", - "9dc625ddb208b1c059f12493751a97855ec303a68b114f2756b842b513192ca4": "000000000000000000000000a1889691e30136d95c0543f516bf2357b282d835", - "a15de862eeaf0bf4bbe7d487b3c73e0062611ff0c2a1c8efb7ee70a07947600d": "", - "a3053687a9f160a16cdb363f9088bc719c812e3777e2124134cc79604d2dce5a": "0000000000000000000000005c4725e00d8f9415e2b77630543fe41dcdaaa304", - "a345ce13ce768748744c207a4c9224691722d4697fc14702db48648e10d69740": "", - "a47177933c8f355f8088754dc8ab101c196f76512e19f784741ed2f96f2c8f31": "", - "a533db48442c1c18882149f8a3b768b48288edf57aecf5122aa8b9170d52dcb2": "", - "a81e2ba117de5a113588647e5864dd9d67d2b04ec1abc35fe3923c24b17ebf39": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "a860d0fcf0b95e3ad7f130c3dcce8fff0d3e98b0d63e6ff2a78d4dc82f6e81a8": "", - "a86d54e9aab41ae5e520ff0062ff1b4cbd0b2192bb01080a058bb170d84e6457": "", - "ab699ccf43a5742809e63fb47016309315b3a496a6ab2938485661d986e0a7eb": "", - "ace33d109b3796ad0870d2cdd24f5ba60631ed1c61bf23082a27000636f0d756": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "ad315e209dd62516ab8c7d1c2d8c3c206525501ebef91d12c34431f9ea255371": "", - "adaf372fcd93e6510620653a95d8b22c5e3c1ac0536d7b2362a5bbb3c7b49df1": "", - "ae394eace5cda18bf10286f929a2ce510e8545db3efe236f48f3df4dd51b21c6": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "aef160683b3dc2aefe89736560824cec0a4d9b028f2fa47cf969747d9dfb3d41": "0000000000000000000000000a74178ec0a865b84eed705e85ddf9b5002389ab", - "b05641b5cfede170815a9efb5af9bc0e545503dba1e869a2a03a952ac316b471": "", - "b17a251204f3edd07092143ea255bd43e0e46bfd2c5d2299d4fcdc87ec0135b3": "000000000000000000000000d8765900c0f467df6bc4f514ed39c568497a8ead", - "b4509c0c8084cd0ab241aeab9996adcf19de6980eef05ea888f88ce18f33339e": "", - "b696031ea0505df7c7b5cc290e50cea0402d2a396b0db1c5d08155bd219cc52e": "", - "b8b5dd07b6aa51acac77dc23bff5aedaa33ec78caabb77a2ba2e4e07cd69c2b2": "", - "b8eb56735b6e0948cdb2059ebe727b7fb20fc4affddf2b590253f6b30884c600": "", - "b9a919f9df630de181473a346f22e332f99b1b51a30a4e3b790d57d37c09f0a6": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "bbe03e3a8ba322502f4ad5d333d0fabcf2ce72904dff3bf9ae471d6ef2e31508": "", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "", - "be48a95e08d0841125714329c6cbf30767e04765c56c0e532ef4c41a8eed9d98": "000000000000000000000000f6defd0f92f2a018ba20bf6051698a8dde7cc949", - "bf1039e9d8f458cb0631edfab4902ce135879eb69e38484e881a6574a68ba9c0": "", - "bf53adb76067fdab0d008aef3ad8b28bbb63c2ce4c2b63394ede73f01a70c865": "", - "bf6d9351d12dad52142cb4f941f972be10a491ad4e76c886f82039f7b45a8605": "", - "bfdc1d6c53b5979b35506e0ea4387c3d4b7a6a4187d5d3a3bf6482efba1224bd": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "c0d41db1b6c5cd39e7205798b17f08eb6fc41ffc96eeaace0bef52fdf71aa9ab": "", - "c2398b32244735b68576beb4f02d12ab087c00b24d1afad52818bd40237341e4": "", - "c2ec347ed6bcf952be26799e2423c44f0c0bc057d9bfca220a74223d57974c60": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "c35d14d63fd65983b84417bc725647d5b2616b0f149f3835bbe92cc6dab2fa39": "00000000000000000000000079e727f2f0f816efd56fc2af37d98af6798551df", - "c41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec": "", - "c46dc67b9717bb28c2a5a6279dcb4bf78042c5098b9784c77e7627ce6433a8e1": "", - "c4f277de8baf6a2718a91d9fd0100c6def50d6193a3900e3c87409599639a83d": "", - "c51a5e32f59750afeabd8dc7c86d2b17b5bc5aa197b43f065638f2263c56b848": "", - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "", - "c655dcc56a8b68fa1c7fffcc6ccec3f6a20e1d55b5a129d89488038c2a85e71f": "0000000000000000000000002182da748249a933bf737586b80212df19b8f829", - "c86655cceaaaf6b16ade4d1054d4e0515cdc2f5225b66d4a8a7386f4ea9c3356": "000000000000000000000000f571eb5abd7da99c6b32b3f3ed0740f6fac7d14b", - "c8b09b6f3384f2ffe0882b9e9717f3637dcae61628a910a73bfe2afd37b15b79": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "c980e59163ce244bb4bb6211f48c7b46f88a4f40943e84eb99bdc41e129bd293": "", - "ca1c89237bece40382b71cc333f0cf90b8d896352d5f336ce534d6875b926e2b": "", - "ca882a554480910ec59c6c0225ca5c4953ea5a3f0958dfdd7ebb428b41eca843": "", - "cbfc9f3ace1802ac45478ea2849968b5f2a6895f1c4beea8e29085ec84a2fe43": "", - "cd2548c4fa14cb15667cd5e5667dafbd0970d09185c619eb3348f5d7dbf131ba": "", - "cf9795aec23e1cac71a092c1659e21eba469f6c64f46e053ac73e6d73bb10787": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "d027c30f3f44c6104c075a1ead1a2e43e9f39198cabec1621f156d4979f19ae3": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "d212513106d90721dbbe3233b6ada0ba201f6c550bd2bcf953720edd84fb842a": "000000000000000000000000b7529ed60a10291754a635ed9fd67c1723f4d83b", - "d4ce672c13a9ee8002ea944e1ebbb620394139c1375bcdb869dcd1271b7c1f03": "", - "d7db60dff9ea1561a36e5abb52e777b0cbb91dee34d46b8a195d9127dbd0dc9f": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "da6b6ac6392a85bbf59fd1c3fb553087a1a3622a8d82a6b22979e0758dc2d0a9": "", - "da974c7fd18b8e48fcd0649bcaa1c3e338128570a18d3b84d5863ee513ebfc2a": "000000000000000000000000b957b0da344f6a17f0081d63be7345a860e5b7a2", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": "", - "daa77426c30c02a43d9fba4e841a6556c524d47030762eb14dc4af897e605d9b": "", - "dafb08ad2e8051263f565108f38cabb76abdca652cbe1b7f859cc22e76f2f85c": "", - "dc7ff32cae6cc2adfe49a8722c521a91d3671fad9169d792473e1560c50bf490": "", - "deb3b6b0bf693bec455d94b929bbce222afe810fe4acebe51ce7948ff4e8f8fd": "000000000000000000000000b957b0da344f6a17f0081d63be7345a860e5b7a2", - "dfcbe054725ea501056a95ff91530c2a83371e15ec9d05619f12c76baa92ee2f": "", - "e07be6bce4cd44b8bf547c4a0f9314e707f42b648525bf6c1cbb5f5689028f1e": "", - "e168b55b543959bfc9a1ba0d6f846406e9c5078c9f20fb49488e72c3781df761": "", - "e2b9f9f9430b05bfa9a3abd3bac9a181434d23a707ef1cde8bd25d30203538d8": "", - "e540257305ba7ed8f0852280a8bb5fa439642e2bbe5efb8396023190d1d4efac": "", - "e85a51ec6b78682038ace16f068a11f783fa49d1434b99ac9ac1ac5f80135227": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c": "", - "e8fb33650faf37535ebd07826e66bdb5c39fb2091055617df310736132d8743c": "", - "e91e6d5af38224d907e455df2217132cc0b9d492350d7c1fcaa48a7d37b4fa2b": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "ec70b555acbe1190ac243b838cadede0f852ce5a7275f8a056ce770e747a2e9c": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "ee5234bbc6b441348cec1b04f7a5aa6034da1dd290fcaf64ecec5733c7f83229": "000000000000000000000000d0277c8a3eccd462a313fc60161bac36b16e8699", - "ee5788e290a48125a7e6875b6150282e64895373632a4d53ae7a8fd1927d7364": "000000000000000000000000a0b29af6a56d6cfef6415cb195ccbe540e006d0a", - "f0d3d1ec19e32ff5b08e71a77244ecfe9b4a82eb41d6f83363322f02a40558d3": "", - "f39a869f62e75cf5f0bf914688a6b289caf2049435d8e68c5c5e6d05e44913f3": "", - "f3db34a47b8751945e9998223fcc0aae730454486da96a78626f4d750dd09502": "", - "f490de2920c8a35fabeb13208852aa28c76f9be9b03a4dd2b3c075f7a26923b4": "", - "f548e71c32522ed78c2588df2cfdc3acd5c04cf930953ecabcc86ee3532f317c": "", - "f88b68a845e59b23d30cfebfb1f42f620d558b80c4f8fe260324b4ff4c364f4c": "", - "f98f8b01ec299350719315489babcb45e99398c8fbaf5c182da442ac83133f1a": "", - "f9df4273f77270a754f076a5d7a72b00bee4b9b9f012b4513637f0dfdda4ebf1": "", - "fd2420d8c819123439502172c23f6b5053814ccb95a80b4494c85a349192c735": "00000000000000000000000006642c4fd062a12b980d2bf28334e48ffe609248", - "fdda011f65897f249410dc9e51464058a1a5f1b48fced4325dd1d5b551a52890": "0000000000000000000000003f9ecb7b25fa567afb2a4c7b633749bda578b593", - "ff0435150397611e919e58bfcec8652d0856528bb4781903439ca1afa83f5293": "" - }, - { - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "38723a2e5e8a17aa7950dc008209944e898f69a7bd10a23c839d341e935fd5ca", - "17bc5666b1ec85903b4657b6adaa1e0d32cce50dfa483c1c5948ebb3d8787268": "73f5062fb68ed2a1ec82ff8c73f9251bb9cf53a623bc93527e16bc5ae29dad74", - "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": "9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2", - "43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d": "3c8727e019a42b444667a587b6001251becadabbb36bfed8087a92c18882d111", - "4df2effad59eb919c30cf175fb15c66618dd52e6865b846c930752d44faf859d": "eceef81a484fea2cd17f305981f7b46d1eb583da375d9c499d90cdd63b650fbc", - "9ea7a819a0914e18e935cf832504011ef2ea4e0d3ee72af3c69d40e2066289af": "cb39b3bde22925b2f931111130c774761d8895e0e08437c9b396c1e97d10f34d", - "a9c584056064687e149968cbab758a3376d22aedc6a55823d1b3ecbee81b8fb9": "af9613760f72635fbdb44a5a0a63c39f12af30f950a6ee5c971be188e89c4051", - "ad4ac87fa8b6f7014b17f40a9d8c5ee373cf34535661d6375156c5ba3a04e3af": "7392925565d67be8e9620aacbcfaecd8cb6ec58d709d25da9eccf1d08a41ce35", - "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": "611ab7d0b1e0f74d9d8e9dad7daae35404c3c0c5d9e94839338562d708d8e9d0", - "fd6d486de5910c769ade60684c4e74e68a93a919b286607d7995398daf42204d": "3b745a1c00d035c334f358d007a430e4cf0ae63aa0556fb05529706de546464d" - }, - { - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "0000000000000000000000004300a157335cb7c9fc9423e011d7dd51090d093f", - "17bc5666b1ec85903b4657b6adaa1e0d32cce50dfa483c1c5948ebb3d8787268": "00000000000000000000000014ef238cfa4075e9ede92f18b1566c1dd0b99aaa", - "43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d": "000000000000000000000000cd566972b5e50104011a92b59fa8e0b1234851ae", - "9ea7a819a0914e18e935cf832504011ef2ea4e0d3ee72af3c69d40e2066289af": "000000000000000000000000dbc100f916bfbc53535573d98cf0cbb3a5b36124", - "a1468185ed02fbc3b21223ec1c8c49605873fc9604653fa6ee049d60edc5ceb0": "000000000000000000000000c4053784867ac0165cbc1f7677386cae8489eae6", - "a9c584056064687e149968cbab758a3376d22aedc6a55823d1b3ecbee81b8fb9": "0000000000000000000000001cf4e77f5966e13e109703cd8a0df7ceda7f3dc3", - "ad4ac87fa8b6f7014b17f40a9d8c5ee373cf34535661d6375156c5ba3a04e3af": "000000000000000000000000316750573f9be26bc17727b47cacedbd0ab3e6ca", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "000000000000000000000000c81b94933420221a7ac004a90242d8b1d3e5070d", - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": "000000000000000000000000a2aa04ec13cbcdddd14a43575f1363ecb1cd90b7", - "fd6d486de5910c769ade60684c4e74e68a93a919b286607d7995398daf42204d": "0000000000000000000000007730b4642169b0f16752696da8da830a4b429c9d" - }, - null, - { - "010d1c26efabac677596f64e2e4f8894606e3636370a8665756ebc184e745aba": "000000000001", - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "", - "033274602281965ae0382e28ea7584792e3d846b4ead2f1529e0d61a63afa9b9": "0000000000000000000000000000000000000000000000000000000000000000", - "034d54cdad5956fb28f9060287fad9073c1e87fb7c6d1709e00f362c9052aff7": "0000000000000000000000000000000000000000000000000000000000000001", - "03c85b4c0caf8657ff29b05743c33f9ffbb8df604c46cc86caa2f051e6a065a8": "0000000000000000000000000000000000000000000000000000000000000000", - "067b3e4d57e1fe2d23c71a95cdd86c8019a649e466d5fcf8e22cce66ceea36fc": "0000000000000000000000000000000000000000000000000000000000000001", - "08774b45b9263bbdd4f040b01c444d167f20686daf1d9dc0be2703ac5b79898f": "0000000000000000000000000000000000000000000000000000000000000001", - "0ea4f1fe8ab439ed65bf736c2d1e75244a52fec0e96cefadf09ea5d170956d52": "0e7de84a5bcf0e16fa56b80cbf55f39877229030e5a8af78a0a78e003cdb657b", - "0ff064739dbad34456d55c4dafd7d33b7e0e7c63b18a1252ef9fd8bc88867234": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", - "10eab4762daae8abab287dd86a83326a78f5bbb8641f38a6792819576c94e35a": "0000000000000000000000000000000000000000000000000000000000000001", - "12292042f822da16c3db8dc1fc978be48218e0fb997cfff35857e16abba41300": "0000000000000000000000000000000000000000000000000000000000000001", - "12fc00c16a89e602782828a84667390d4a14bf74c1fd16dab784c7ebae45de06": "0000000000000000000000000000000000000000000000000000000000000000", - "130508100dd1f8b1c72b7eb16f82ee3b8a6f3bcbc8461c9f02c06f14ead89d75": "0000000000000000000000000000000000000000000000000000000000000000", - "1403e9d046887460408ff2c1956a064def64c4bb221b7bdff146b097b3a282c5": "0000000000000000000000000000000000000000000000000000000000000001", - "1669beee2b904266bc5a6eef210d6869ccea5c9a25e173d940428ccfbd6310c4": "0000", - "180fd71813c81062f2cdf852bc03fb8e8b8b98d21436001e255dc7141cb299d7": "0000000000000000000000000000000000000000000000000000000000000000", - "18c5b4c6cf9fccb97b1cf7f966b2ca8e951365e13eaf63ec93d3fd4ad87fadb5": "0000000000000000000000000000000000000000000000000000000000000000", - "18ca0a599a7bcdfd7e487cafca8ab1dfb6ef18074841fc3da95b73479eee3270": "0000000000000000000000000000000000000000000000000000000000000000", - "19a1b3731620b60c6decd10c1019cd9d6fca4fe6025d6b7307827ea994dc81f8": "", - "1a949941ca6b99c817a4e16cefe478308222535d19f1437ae2fdd892cb8ac789": "0000000000000000000000000000000000000000000000000000000000000001", - "1d885466c9ed74249248ad01d298eb4f509d9102a849d767f893da1e78799a9d": "0002", - "1e2d85db0c7c63bcc2054bf2d3caaf5d61920c77bb16f10572e6115458b0c352": "00", - "1e6d8ff8f706b4b423086b9c725a43e4aa6ce504b790612fb0e6f738cafa5b00": "0000000000000000000000000000000000000000000000000000000000000000", - "1ea137b55c9c2a68d4dd701fc31c5a11a2fcb3f9cc4c2fe6b2bf968d250ece35": "0000000000000000000000000000000000000000000000000000000000000000", - "1fe8359bcf1145edd7e23e4c32df953ea82d5db7a0d840ebe1d436f73bf93c5d": "0000000000000000000000000000000000000000000000000000000000000000", - "216ff577ab4b6adc22902060367bba5d514a67cb53b6171bc62569e58ce1a8ce": "0000000000000000000000000000000000000000000000000000000000000001", - "21915510d6f6b58738bc790d97df3881396743ef975eb2a4d7dd0cd4bc10bc4b": "0000000000000000000000000000000000000000000000000000000000000001", - "24137ff708807fbf454fa0f7c3b603cdcf414890396424962389c3f887be5c00": "", - "244fa8474b97c1fc5589e3372062373bc939e2c2c8d51733cb62dd1af06d93ae": "0000000000000000000000000000000000000000000000000000000000000000", - "251828ea0fa55eeff4920c784f29d02b3d462893b0a53b35da407951f670d4c3": "0000000000000000000000000000000000000000000000000000000000000000", - "251f23d45faa62fd4edee6c7ca9d0e522dd618b21ca79c0ececcf6b4ab1d418e": "0000000000000000000000000000000000000000000000000000000000000000", - "2559c03f8499faccfb5d86c5a9c6857b9882045939b54fcab26046debc586400": "0000000000000000000000000000000000000000000000000000000000000000", - "25612858eb4e5086b12e02d2fbb70b8b64bb55022fdd629ec789f2c84607688f": "0000000000000000000000000000000000000000000000000000000000000000", - "26d4791888c6a5a6dad93c613727e1025a5e629993ace97b6f60a168ba5b643b": "3e", - "274f35379f9c71d56060a980068aa27bbb6b0163485b566c98f23208d9afebe8": "0000000000000000000000000000000000000000000000000000000000000001", - "27611fee3bcae57830f4acede0e4ddc28d6b80307e4e3c24518ff8427870b1a2": "0000000000000000000000000000000000000000000000000000000000000000", - "2815ce44f31ea08706a5b434ba9241be790bb99a9a14f2abc58f44e4c7ed5258": "0000000000000000000000000000000000000000000000000000000000000001", - "2bbbf3147d86f35e028afe730dbe8a309c343a5af574d686b4de6f0299f44b62": "00", - "2e2b0152d440cb2300acb1a66f126dc6a55d48429cd4d1c65a6b08b1d70faa1a": "00", - "2e7668b70ebbbf0427b7206105b43be56ed44866084aef806b455c4302bb09ed": "0000000000000000000000000000000000000000000000000000000000000000", - "2ebcffbe9bfd10639c61895d63dc4adbea44b1ef949f81ec56b684a553ac3325": "0000000000000000000000000000000000000000000000000000000000000000", - "2fa614a47cc9c85b69679204e1ef9ac927b19471ded492b8ece1e8e67262fb51": "0000000000000000000000000000000000000000000000000000000000000000", - "31d7d7f3d30e2f6915813b0eae348106f9c36033685f734d666df55950721e09": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", - "35de80ebb4a020482ef20b08e76ccf3047a069603f0f298ca69d20d652e8d84b": "00000000000000000000000001", - "39d0581989c293b89482bfa1e116cec4009a18aa00b89c5242b7fe5c3766ed7e": "0000000000000000000000000000000000000000000000000000000000000001", - "39e4706fe515d7364d5d89bca544e81f9ef187062620ac4e97b4c64e9dba668a": "0000000000000000000000000000000000000000000000000000000000000000", - "3b41faafc90d54978545a2f7f0a0fbc5710b148872dd6e1d6c2ec4c94fee3617": "0000000000000000000000000000000000000000000000000000000000000001", - "3b73107bd69d6198ed057833aac40d0dcdc99e568c5e4f2ff154688bbab5fb26": "000000000000000000000000000000000000000000000000000000000000000001", - "3bf09109c39d56015c8905aa26907c8f8c509e4a54ff4efee2f896a8b1176042": "0000000000000000000000000000000000000000000000000000000000000000", - "3cc1cc331ba2e9897c734d97dfb178fecedbc4cc90c706eee0c6059c94b36297": "0000", - "3d8f5d715374acba3f92a425b8bad30eae5f2f534a63c4cea5ad4401fe46ad75": "0000000000000000000000000000000000000000000000000000000000000000", - "4084709b90b82b198ec95c387a53f65e5492611a56a9bf34a46d7c5fa02907dd": "0000000000000000000000000000000000000000000000000000000000000001", - "418b0655db32fa5820ed237053dfe34869c9a1c39460ed8d58e6e87e5164f8ec": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "4262e4bfbfbc25391172acaa697e2b36c9aa60be165a81de92f9a6be0ed447ca": "0000000000000000000000000000000000000000000000000000000000000000", - "42c4e39cf1e2224199b3895508025d4cf89fc21154dd0ba0c9f86ebbe81667a1": "0000000000000000000000000000000000000000000000000000000000000000", - "42d09d5551133d4982c7c496aa710fdeb46c2974fa2ee405bd0cf2d8687f5eb4": "0000", - "46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21": "", - "469eae86b1701336b3d583df4527d639b1cccca716608b728563a0eeeeeb55ba": "0000000000000000000000000000000000000000000000000000000000000001", - "4798efaf9795653086f314b2a745a3c677befc3a143f8e71f6961410234d4aa7": "0000000000000000000000000000000000000000000000000000000000000000", - "48cbcce9cddcfc87a9dfb28f8c07a498c6cae91c3577410009e009de8ae15426": "0000000000000000000000000000000000000000000000000000000000000001", - "48d102df204080cb58f6131e8de27892647da0958ab111e3b7604279fe7638d9": "0000000000000000000000000000000000000000000000000000000000000001", - "49daba1e38b9fdb769584f02a5cf0d5ccc52e1dcc5f1683c384e308cbda1aefb": "0000000000000000000000000000000000000000000000000000000000000000", - "4a4e7e22f228bbfeef1e2e652aeb39630a3fea4545ce98694e34b3a13cbf4d9c": "162ead82cadefaeaf6e9283248fdf2f2845f6396f6f17c4d5a39f820b6f6b5f9", - "4b1c0353029df7c0e24c97c1caa608b2447a736abd6ec92c6a6ad19c74af6764": "0000000000000000000000000000000000000000000000000000000000001c3b", - "4c1451f2f475e314e13ab026083f0e75042da3757331579bcc2acf982eb93e47": "0000000000000000000000000000000000000000000000000000000000000001", - "4d26511722a244d920d5b388b5008248c71b7232df5e2dad0c68731ecea7baca": "0000000000000000000000000000000000000000000000000000000000000000", - "4e895772ea04e2bebc4494c0f61d649af0ec15eb35e3d6fb23ccd43cec8fc477": "0000000000000000000000000000000000000000000000000000000000000001", - "4e9bc8aacd818468cd54729198300a542eaa7ed24d172b0fa62eaca18c80320f": "0000", - "50f0b7d9f0b0195307199ecc68ea12ec8b2855ebd3938241acea7eab945706a3": "", - "5627aa867eedac80321e8a99321c11d624a974907a00338d183e495737ad224b": "", - "56cd39d56e15e186eb742169736637742fe0129e3816caa42199e1d11444a087": "0000000000000000000000000000000000000000000000000000000000000001", - "57f24ba6197dd6bca4b92b2b069547c4d9715cb715b118fd06b6c506f3fe9a6f": "0000000000000000000000000000000000000000000000000000000000000001", - "58dca178acf545c5f569bf230c25f4b299df65d50427e5599c062de84ae8e117": "0000000000000000000000000000000000000000000000000000000000000000", - "59083a31c083633493c1f0fff98188fc553195939b97664ddf4f1f7372fdb9ed": "00", - "5913b435dbacbaf8a060c4901cb61a7aeea4f64e04bed5194a9cda0e3f7407b9": "0000000000000000000000000000000000000000000000000000000000000000", - "5ad3326c83fc81b119a19e072abec9b015d3230407d61fddeac53d8d622665d2": "0000000000000000000000000000000000000000000000000000000000000000", - "5daa348868b1c7f027498f63593dc226890161a6e9fc6a458299fb2d954354d2": "0000000000000000000000000000000000000000000000000000000000003e80", - "5ec71d7c8e69482d0e0118734635e881ff05f5a87604da2302f19e3ade457b42": "0000000000000000000000000000000000000000000000000000000000000000", - "5ef61126f34e679f7b6d859b4f20cb716481fe3e7003d0bcfb017694f6d28649": "0000000000000000000000000000000000000000000000000000000000000000", - "66b7f8ac5bd4017cc1ae82dc7a1a5334bcb127a1f64ebe4f1a7595196da10a58": "0000", - "67f7c6a923a7558c8cb23e8475dda653460be6ee507466645d79d5fa6dd5c63f": "01", - "68ad0981c9f951bdbcad78d21e35e5ec7918595b341c8b3dd81d5f50eca06b78": "0000000000000000000000000000000000000000000000000000000000000000", - "6b1061285efade3c0b2593286bb08db69ae28df9e1d6714041b063e362ba1988": "0000000000000000000000000000000000000000000000000000000000000000", - "6beafa77883efd2c221ea4cb29831d1fe10a2190379312217dbe57909ac356b0": "0000000000000000000000000000000000000000000000000000000000000001", - "6d835e80050760971242c410fa7ddaf8b268d3669e2db343fd61343cd87d80f4": "0000000000000000000000000000000000000000000000000000000000000000", - "6de36875921c76ab5434e64791cc62395d65f49eb21f9ddaa82aa8756b44ed3d": "000000000000000000000000000000000000000000000000000000000000866a", - "6edab1ef76af731a34f9a306bfdeeaefe48aa8f4826c329e1fdd702652e17927": "0000000000000000000000000000000000000000000000000000000000000000", - "6f64f08deaeee1f2aa90289b4aaafc4d76bf5d79a6f0aee01cce6f914d6523af": "0000000000000000000000000000000000000000000000000000000000009c00", - "7030a0dcea1e544f2f20cab453564c3f7343ea3c7949e30b35c9b562d2a6f532": "0000000000000000000000000000000000000000000000000000000000000001", - "72937ea411f48b90764786c97d4229d192cc6b1426fabdc86b59298816af97bc": "0000000000000000000000000000000000000000000000000000000000000000", - "73291d37d5bdc108723a04c171d113d6c323c46000569d0eaa319e295497a911": "0000000000000000000000000000000000000000000000000000000000000000", - "75137d2ac452fa56c54f3a84b6a5032cf7e9979578b76da3d8893684ffc3c0c7": "0000000000000000000000000000000000000000000000000000000000000000", - "75e5c2f08ba7f4f5196eeee2fa5d4585c1a7d13d94c8dd01d8db7c6664bf5dc0": "0000", - "7687fd0273528d2f1aa49723afefa95a867eaa5a6a898a75d8d1c1774f244cdb": "0000000000000000000000000000000000000000000000000000000000000000", - "77d6ede055e1bf3669db1b8e965fcced0a76864f364f6135e6cabe1037fe4809": "0000000000000000000000000000000000000000000000000000000000000000", - "7870eaca19c10f38ad151dabd337cbc8e2df484ac4801b07b0d80348b645931e": "0000000000000000000000000000000000000000000000000000000000000001", - "7bf0fbb8f1754a19c5efdf2dbdc4288c2d8728852734b30dc057d53beb59e5de": "0000000000000000000000000000000000000000000000000000000000000001", - "7d24c042d652db2f2d9e2e5d1eae97d19b44109420f11b6eadb6b90cb1ba50ea": "0000000000000000000000000000000000000000000000000000000000000001", - "7f2686134db3a520c34fdf758eba9a0d4f9c8945a3f06d1f4d7eb68b433be142": "0000000000000000000000000000000000000000000000000000000000000001", - "80de6c463032565ad835285aac5101f9748b93dde6ad45bb1136190bb8f5c253": "0000000000000000000000000000000000000000000000000000000000000000", - "8187355fc5e82694be920349dee28c7d4ec2a7bc15a85db4db530b13f34b4050": "0000000000000000000000000000000000000000000000000000000000000000", - "8191431cab44389a73030b5819d7af3bcdb3cb49e618810032cb1fd4e746bb41": "0000", - "8257bcfd8eb8eb906d8f3a0f3cb71577e6267f1a4af7f3fc611dc19dd96192bc": "0000000000000000000000000000000000000000000000000000000000000001", - "8507835cf8ffeccb9e5a2ef45b957ae5f463861884bcd279214fa36b2965619b": "0000000000000000000000000000000000000000000000000000000000001c3b", - "86d444d512bd04db648bc23e024132fbc2186aa73cda0bf0741114132324124b": "0000000000000000000000000000000000000000000000000000000000000000", - "87b97726f9d1d6e04d170320fc2d8b16a971ecaa1048ac38dabab51369bd4543": "0000000000000000000000000000000000000000000000000000000000000000", - "880480a0dbe4cddcdb5cea5a9d483524b2f6c4e7b492bfe236b8d15649d0673d": "00", - "88451def3af9b5d137d0082f973d90facb971643f4eabf3c201a634861facef4": "000000000000000000000000000000000000000000000000000000000000001b", - "8a068cd204782a5859890b4201f784e3ade964c45f373bc4e20f2f0c60c927f6": "0000000000000000000000000000000000000000000000000000000000000000", - "8ad16be55060aff4f690974f6b8c74de388599e798c8ff48261897a95729fb9f": "0000000000000000000000000000000000000000000000000000000000000000", - "8b4d99f7357e3cf1c7ad74cfce0c1bab1ab69920595b7bb2c20ed919aa03f77c": "0000000000000000000000000000000000000000000000000000000000000001", - "8bf994b1ef100d61c6b3857392be820ae7855cc194cfffa90d3b43d0a28b13b5": "3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab", - "8d69df480ceb95c657631fe973f8777f032a807fe36e698458813ae8ac5276e4": "0000000000000000000000000000000000000000000000000000000000000001", - "90ac1c964e1edcf937f9332f953f971919ef44a105ec663be0eb6707ef52de9b": "0002", - "91bbd8d35060a647704e6e8410263135b9df075665d0286495fba2ad2129d178": "0000000000000000000000000000000000000000000000000000000000000001", - "93d390c50ad9871ea4b3ec9de9c3f10646951691931e069e03c0de0675bc6b05": "00", - "96cf34086b42afae29477ebe8d6eeba6fef1da1690e0ea32d8c4947466324569": "0000000000000000000000000000000000000000000000000000000000000000", - "993d6040be7987f83bb56811395b7d76d577bd885b943d2755b44fda0b44cf50": "0000000000000000000000000000000000000000000000000000000000000000", - "996f9283a0477c8eaddbd50c4a9927c80b0281d3193538469f79ae95f8ec2f5e": "0000000000000000000000000000000000000000000000000000000000000001", - "9ab09ec74fdfe6a0f390d51b70f6b31a9b99287080d91395864c689ec8ef96e1": "0000000000000000000000000000000000000000000000000000000000000050", - "9c3a806764dc757a3c4f3e4ab27f2d645f13684a5d0f65b3e527fd144464d052": "0000000000000000000000000000000000000000000000000000000000000000", - "9ce2281a0a9ead36edc758647abc060766f200a5210d5b97a9b7d1752578c3ee": "0000000000000000000000000000000000000000000000000000000000008615", - "9e24cd89db1e412aa85e71e9ea955e00a1c22d66716bbd19fc92df1a122a71bf": "01", - "9e6a8ff68b83a4a01c2c344f507ab1c738cf5df1f6f846d47fea6f5d397b06e4": "000000000000000000000000000000000001", - "a05b578a2942544883921581b74d7bc5f44de59ae5d1a3cc21626658b5d9cf41": "0000000000000000000000000000000000000000000000000000000000000001", - "a4ddac19e1b86ed2a29ecc5ca2e2d840923f30380f5c2a732b746427b97c7970": "0000000000000000000000000000000000000000000000000000000000000001", - "a923d0e400cbbe1e0915c5a699cb8a51578ae87755f1aae7929289166ff23f33": "0000000000000000000000000000000000000000000000000000000000000001", - "ab76f1d5488b8542336a386818cf99412b7aa17c589d244c29f9ae9cb3901815": "0000000000000000000000000000000000000000000000000000000000000001", - "ada673786089f911b5dc7c5fc3405c206e6e7564e4dd4466b563d03e659e94a2": "0000000000000000000000000000000000000000000000000000000000000000", - "b07a3d24561ccfb56dd51bed4d640a0f00d5d86fe953d986846be0930c35157d": "0000000000000000000000000000000000000000000000000000000000000000", - "b0e9dd0b5749795680b0cc0db0167b85f1d2417acf9b44869d17a876a4f422ba": "02", - "b2f231521511b65fba5f194f65a93426b16df90c4ca4bbf0b51e2c3b02d59b2b": "0000000000000000000000000000000000000000000000000000000000000001", - "b5a34ff06af3b01441b6ee4ffac60feee6f0b63ed6be80a21c8aaf8b9e7f0b4d": "0000000000000000000000000000000000000000000000000000000000000000", - "b7c17933a8cb769998187b25ea82c3ac8dffcf3d1976a60b023b9d06c0cd9a4f": "0000000000000000000000000000000000000000000000000000000000000000", - "b97681ddced4ee8f4efffc5b58044de3100b2dc7c1f7f6b72161200f10b0604c": "0000000000000000000000000000000000000000000000000000000000000001", - "b9d52ce810f7c808324c717f1a5b612512a24a3227d311f508edec3c931344ad": "000000000000000000000000000000000000000000000000000000000000005f", - "bafcbd8a4bbc5837e3a637e432c4ee0a48e30a6b760efaaed699aed606b0c768": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "bc2ae76808e3de30404a4dd4221f3289cf20a272161d4f8c0c58c2e736706256": "3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "", - "bc8a5df0db3fc6a076ea676a8dd5f6689777de551a235d75f77a520bd243208d": "0000000000000000000000000000000000000000000000000000000000000000", - "bd59618a09cdeb7538ce8cf97820ae2ab18102ac903c34c7de6d72c63c3d0f08": "00", - "bd66e927d01f078a67ba5c98539a6a2f79b7187457430237f7d406ff12558deb": "", - "be17aa6916c3d516ea45c2b8fc312f6bffc47f14325b7a9005786f878e1bb3ba": "0000000000000000000000000000000000000000000000000000000000000001", - "bf528ce5e535e2cf241dc944b153d5925ab2cdd488c386e326716b6a8ecf751a": "0000", - "c1ec6ef309c736331efc3000afc186d2021944309ee6b20a4bf3bc735b7aea75": "0000000000000000000000000000000000000000000000000000000000006801", - "c3e4a3530ca51ae69ee63be92701c84c6ebd820e1f89ca15548c94dbb851101e": "00000000000000000000000000000001", - "c4527e59efc328e49b6926353042b1758cc422461230653e83a433634155adcd": "0000000000000000000000000000000000000000000000000000000000000000", - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "", - "cba5ff6688618456c8228b8c2bfb4ed254ad8620b99ef3ed8fbcb5bc66feb478": "0000000000000000000000000000000000000000000000000000000000000000", - "cd223a9e187147d1712bdeed93889e2edad86091108b10bb97790428717dee31": "0000000000000000000000000000000000000000000000000000000000000000", - "ce9b2bf13532af4013c467e57479a71625bf1a7c8190393f29ffe8d3f3275aae": "0000000000000000000000000000000000000000000000000000000000000000", - "d09419104ce1c64b6a06bcf063e98c2c91ad9e1beaf98b21c9d4734b4a3c9956": "0000000000000000000000000000000000000000000000000000000000000000", - "d6c0c03ec1f713b63be3d39b4fa8ef082b3407adc29baf74669fd2a574c638ac": "01", - "d837f9dcf93155fe558c02c7a660edc0cd238a8b8f95ee6b68e4a5c6a41fc70a": "0000000000000000000000000000000000000000000000000000000000000001", - "d9e1670598e5304eaf7da7b328570d20fe98b6977064f5b922c361d5c75751b9": "", - "db5d4994d6dae845a6fd76e91ade4d3a35f270d86673ccc32405042c3c3d37ba": "0000000000000000000000000000000000000000000000000000000000000001", - "dbc6ddf9220c7ccfc94057fd3d25b712a0d7f51c3ff44e291458f2daf4ef2ec4": "0000000000000000000000000000000000000000000000000000000000000000", - "df244f503af99f62276115256c481d3cf0786d477553cf0bb25df1579dbc9ee3": "0000000000000000000000000000000000000000000000000000000000000001", - "dfab0a2d13154e415f4f999b6570efd185de14a2cf07a14eb543feef7542a5fa": "000000000000000000000001", - "e0aed3305cfbf6b99096f7ea895c0f2d721dea396d08c75c892152e757668eb8": "0000000000000000000000000000000000000000000000000000000000000001", - "e1223738398f40c909a2f63d7f617ea86f1c6b8bd82786ec6e3ff0a8729ee803": "0000000000000000000000000000000000000000000000000000000000000000", - "e1d19f16b8648afb5fd13c5bd437554c9d1bd61855b47cf4069d6068f27f90c5": "0000000000000000000000000000000000000000000000000000000000000001", - "e6f0c93cd48787b5c65c1df991ab038e7fd1512ae6fa680ca79051348e4bd2c5": "2b", - "e7e08425a2da0d12f7d6cdefc8722e15f577c8b7908924ef5a6f09dc8a8df381": "0000000000000000000000000000000000000000000000000000000000000001", - "e9321ede4409baf4d20ce11283a0267a934062cd546b6fb0097d755686ff0194": "0000000000000000000000000000000000000000000000000000000000000000", - "e99b55f3b486798837a08b10ad2cd917ab907b7c3d62ab7cc31f0d9aeb19c043": "0000000000000000000000000000000000000000000000000000000000000001", - "e9c48c3e8bcc466c06599e85e1209d23de86ec441fe5ec9b1042a6275f2c818c": "", - "ebd3e19945cff9b7462359bb882a8005d9d1d69d9f82db099f29508447ec7b59": "0000000000000000000000000000000000000000000000000000000000000000", - "ece1937784110ebc937ef343c108c6385a8b13b3e8d0617d30bb6bd95701f270": "01", - "efdc1fd8d4c7bc0df6a2dcd82e30e65d2964daf02b52c51067c3d2e525943f18": "", - "f0001b79c0ca5c8fa824f187c3e987fd066688f64c1763648df46d59bfcd7a32": "0000000000000000000000000000000000000000000000000000000000000000", - "f109ca1998eda403604fe597551159daf3bf1c60b6a9f28b02c66fea4594fa33": "0000000000000000000000000000000000000000000000000000000000000001", - "f206ef1f61673596e4abc05dca7be9148dbd34841d85584e8c41d75ecdf5a936": "0000000000000000000000000000000000000000000000000000000000000000", - "f344c28b47c6de556d35ec1e82c6ec3d06f1951d36aa1cd7351d4255e7f04a9c": "0000000000000000000000000000000000000000000000000000000000000001", - "f3f136516fd855c4cd4bd193c2bc38fab112196b023eb22510da3c6cc276eb72": "0000000000000000000000000000000000000000000000000000000000000000", - "f402d2cd2eaaede36f70e970a0e397dcf76e4488dd6304bcbc77f8ba8bc423d0": "", - "f6183b77357ae6ff58dc23d48b3f1d9e76098e6f884751ed03a261a0e7bab231": "0000000000000000000000000000000000000000000000000000000000000001", - "f77262efc56997fdcd4a91cc1449b01f93ebca8c758efcc1c85027e69cc6618b": "cd433bbd1fa6457602a79d957ee85a37e2496d0a", - "f9adfd03d4d3cf7e9245f9677b882459f0f0ea492b36ebbe34fe965bf3b4e05c": "000000000000000000000000000000000000000000000000000000000000002b", - "fa22beaea48d8a5e37b2f00efdc83600d4a9d1e1ff5a08cea987db6e6d168833": "0000000000000000000000000000000000000000000000000000000000000001", - "faccb2379850753cd96f3a4f37dd3f431863cab480cd1f808f696b0cafab5da4": "0000000000000000000000000000000000000000000000000000000000000000", - "fb859356d00b38d191345159ca428ce6b47c7c03000e91d93ba541f2db07ee75": "0000000000000000000000000000000000000000000000000000000000000000", - "fc08a758057657032bc36068292a2e3dfa490f21697d4e5841ce2bcbf50b12ba": "0000000000000000000000000000000000000000000000000000000000000000", - "fc94ce88596dbda4103ce1ee25fe0f05e0d8e1a57a2d8660d45800ef89ea2f64": "0000000000000000000000000000000000000000000000000000000000000000", - "fe0d647b508fe5dbc76db4136a295fcc960ec1990c7639bb8ddcd06c1761e9e5": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "087853ba5e0cab850ae6242dde6d98ef48e6d88902ad81639bafa8a34d6f49b0": "059a381fec09e29448a58ae8905f41d1eb8ff0ed755aa0f827821aefde02ec7d269d2516bf8c4f5798cc1267162e59add561e5537a328fe0f28a252fa287a72a", - "089579a4ffcde57eb85cde7bde7a82070763b1f99942cce953cce0e2e9df83e3": null, - "0b4163cbc5404e24a3d2c9d78e8f289eadd9dbf93f4a5a823bfdd0d2fc9c1468": "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f", - "0ba4ab054e27484052c149d30ceb22514904ca1576715ab4144cdc33ab4f4fdf": null, - "1e990e27f0d7976bf2adbd60e20384da0125b76e2885a96aa707bcb054108b0d": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "1fe4b678a932ecc673c8da1eec72a4015be345e71eeffd5fa56ebf858521bad0": "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4", - "2080ae5ce684e2b718fb2b3d44ff77943c095f21df08c8cb5095eb9dc74b9f66": "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b069610f239e3c41640045a90b6e1988d4ede443735aad701aa1a7fc644dc15a0", - "209b88501c3e213902ac4a0107d718a82fd1ab47afea7df282097e99614e5d42": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba", - "2a61041edd2708655ab16256060cafcec93e715b457d1f118a16bc8ad0dbdc6c": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "3a709301f7eafe917c7a06e209b077a9f3942799fb24b913407674a4c1485893": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "4169dd4e123daf8687a2d5c3d192bfcd52fe5930697a65825ec8a4fd9de3db49": "1d78954c630b3895fbbfafac1294f2c0158879fdc70bfe18222890e7bfb66fba101c3346e98b136a7078aebd427dced763722d77e3d7985342e0bffcc6ea4d56", - "43e0f2c14b3a7e08fbe006fb3d772d75a1c40332c5e1be232a3d72a324bc0959": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "4b6a9063777e44ee27179dccf5599749e0baa322c4766b54201fdb8d41dd42a0": "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1eed5d5325c31fc89dd541a13d7f63b981fae8d4bf78a6b08a38a601fcfea97b", - "4ddf918eb9b12ae0cd9aeeac5f79a53dcf167038c1e155bdbe3d0204c3dfd24e": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba", - "5446fe9042fa6576c541fc1f51dbfca0da0808a0e9f7a4f2271092bbfdf8113b": "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0", - "59a9b598fb1cbb4470c1c83c3c7919723927e32dcbfc499369618883f7716c8d": null, - "6100b59abaa8604b0a5d6951ec4baaf7b93496bea06ee940866fe486fa7b0baf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "63dfd64b22e681f0cc0d54e30ce03421b4da05a2a8353d36c5df9c9dd40fd0e1": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "6512b6e482081b0b3ea65d88b8b428c62ff6b89e46f69186c1d58a754f93e541": "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f", - "694596c3962bbb9a958855415014b10a87c287659db9abf4aabf5fe2010b1a97": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "6f5e8cbdf88b245fc2759de4436caa2738abc9917d104672bd7f709bf241ca4e": "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad", - "6f8ce260b70482f55afd521252d239b61a0f04064cb4d071b1008aa3b85869ef": "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b29ce3d80a74ddc13784beb25ca9fbfd048a3265a32c6f38b92060c5093a0e7a7", - "7fadc430d0dec6b6e0f213585c75955d9ef7b7370ef129c286f057cd5178e000": "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97", - "818901c99b5392251e6a8d13b1a8a9ca180639b65d7e744706237c541757fd0b": "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4", - "83563313efd2835227e5d96f709bc84c8f7609f0e03b8e5396f007229f8ddd41": null, - "8414e61c90e5e365475ec73cdb9e817ca321ac4f089c731e307e8ed4ae7dab13": null, - "94053252705d2109c767122b07092a83795f2e01592208eb24e06bec96a62cf5": null, - "9611dc1757b109073c8058e4e117a856676ba6868ee12dde989c8eb584d6c31c": "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf005acb4b400e90c0063006a39f478f3e865e306dd5cd56f356e2e8cd8fe7edae6", - "98fc7813abc874e5ede492594e5f9c17d2e4898531ff7ca5cb24abcdaa9343da": "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db8", - "9f3654a7131bde2ca3892dc6a744102195844e30c3cd887d21bee2b478fc904a": null, - "9fa94947233ff18c33a7d71ea5e70bf427dbd099f535873033e71bbbd444ae8d": "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a", - "a446f380799bc37eda79425d0e5c0824597f999331811b6774ad5c35e49a0114": "113aeccecdaf57cd8c0aace591774949dcdaf892555fa86726fa7e679b89c0670bffba84127a19abde488a8251a9a3fce33b34a76f96aafb11ab4a6cef3e9979", - "a6ec34af2b0198fbeb64e50ac8b0f2da428489a054fc3732cb7c6c3c7f5f6c01": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", - "a9479f519cbe0f8c355898abc7c22878253448b76f3cb6dc97c3ce334d1e8e59": "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f", - "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "b033f51f4db914bf9d626769036ac44b44c1b724fd7b44fef43e335b73c1d270": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba", - "b89869015f780e8a53577c7f6e2405031aaa7660517b94e1e074d8edf1f87d31": "1d78954c630b3895fbbfafac1294f2c0158879fdc70bfe18222890e7bfb66fba20481b2bf7a68cbf47d796f93f038986340f3d19849a3239f93fcc1a1192aff1", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "c86bb6b92c0c1c6be3ae712335b967f18e191d583ac7ad81ae7bbf62bf4188ce": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "cf77bc69c2c3bb37003db39ef18a918dd96c66332a8429c097ee2df63d913dff": null, - "d41981a62a65bc8d3ec7e86aeaadf0a5069f58c61705f64ead9d6a33f56f6b5b": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "d4f54b594db007f1fc6b71bdc64a6c9f1c2606781b50f067a5a61e9fc97d639d": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": null, - "e59dd4bca360eab19889fd0a947eed0cd133f03d9e7607ee32ac00275583ac19": "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45", - "e90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "ecdd2ab037bc00abba4fc8f792146c265c2fab8e61f473576390d2a1770073a7": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "ed1ce265dfecac5ea3c399a8fe98850a7783ac3dc659fc05227a531d6a021cf1": "113aeccecdaf57cd8c0aace591774949dcdaf892555fa86726fa7e679b89c067246493eeceb7867dda07bb342fd7b460b44635e9f8db1f922a7541a9e93e63ce", - "ed625fcc26177f39b4fc71b3ff53137a323292ec20deb963562edfbf464e30e9": "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf02ab799bee0489429554fdb7c8d086475319e63b40b9c5b57cdf1ff3dd9fe2261", - "f19ca958b80b85694d174bc382abb57982336924086e48c18fb2f4df89f56d0e": "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4", - "fb407753c8e83acbc2864aaaf46fce7e9de416d8c7f9acfb23a2904960a0f778": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", - "fd6606457d19fae43d0564a64332e8ef149d6831958e2af8162b1f7ad02c9542": "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1176f11fbb6e80611a7b04154401f4a4158681bca8f923dcb1e7e614db7e53cc", - "ffea936086ca95b2dd963e8ab813c77474fbb3653c54c5d8920323f7932161ab": null - }, - { - "0029fb025a294d850032d464d2d64ca320feac7242d52b76a2dcad5aef6b9074": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "02538696193579f388ba022d22804a1404ec028970991c051088c527022ac9e8": "08e2142845db159bd105879a109fe7a6f254ed3ddae0e9cd8a2aeae05e5f647b0e5345847fdd0656d7af3479dfd8ffba497c0af3c59ebc1ed16cf9668ee8b2b5", - "04ca9ed1ab28cdd7c66fd06892abe6782dae89629b169696641822da6e5e38fd": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "053f3d9211b10914f2b89c599a756d65039019e769c52e0fd1b794dc5b7739b2": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "058c714e4d5a9e9939de6ec321a6b4076c9d3a53f8967b2d8b050a30fea97730": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", - "067c674da350d4cf2d99ad49e040d5c4676b37a4eaba81ddd871ad0e56d0d9a3": "03d64e49ebb3c56c99e0769c1833879c9b86ead23945e1e7477cbd057e961c500d6840b39f8c2fefe0eced3e7d210b830f50831e756f1cc9039af65dc292e6d0", - "08c5246e29bb21092d615664edcca7ac43502f639c6eeb3ca6b1303ae571db3e": null, - "0a10b8f8bd270b26371eecf64c6ce2fc516f0b0396f26a599c3f91be0aeba443": null, - "0dac7d7f3ba7feac84d47bed8b63231f6e0286ac4c8a3e0e630a2363f207c0ee": "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4", - "0db604ab45b2d4fe2a1d9eec33fa159203e0dc9f7ad0bfd9fe934cf2c11f65dc": "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45", - "0f682b3818529f9588fa7f9a9551e70b5e93f8e3c77ab93a763b7432e2877fcb": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0f7b6802eaa6e11662f31fce71ad891d2514d724f69e365fff2c910db0532582": "2c15ed1902e189486ab6b625aa982510aef6246b21a1e1bcea382da4d735e8ba02103e58cbd2fa8081763442ab46c26a9b8051e9b049c3948c8d7d0e139c5e3f", - "0fd10c27b65425e0e1197ffc9e8294dc9baf81a08c56be2152a9854e7e64c086": null, - "1c10b4ce4b4d5514c9ac638152d9ebe22318a39b9ac0d559aeb8bd1a386cf1d4": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", - "1dae2a9a66d8d6038e6817750576e21ecac9d54b76c456f5b6d69b15522c8b2f": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "1daf3e40d9d490b1eebef01961ded71d1cd3c851e2a733da5f8cf905b6e7bcdb": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "21453ecf7fd9f9f354b2f05d5ec3522ec9f2bd2b9b2093c3ebd9a7b057689105": "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97", - "21c182bf3a67519743a345c8710c8198773ff75f242737e4a31574ce4cafa43f": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "25709ff08e817e4d69c96350c24d3b65079a6416b07e8b7414e2a5a270a726a4": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "27eb17d6b599328c754b46de3dda054eab691847bc798d78bd16c74bffe0c925": null, - "28e74e3cb79a90cb50c547a88cc180925d72509a14443f959abd9bbeb267d2fa": null, - "2a0490880ca14797393b4cc6dff702ff4b7d1ea884217bf1c01bf61cf775e448": "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575", - "2cfda879dcf534565d413e4bc65be1b6443383eb23288610914a2e85c33e162d": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "303fc8635e73bf76eef7e6f894ae3944df6c09f537c2b58e7d650a3547db691c": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "30e197a2ed4381e5653ef28e7968985813e2d94b72f311dc3bb0e2c37e8acfb8": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "35cdf44288e372dc3e23cba44f9bf2af11728a70e9b43457c8f7a2daac0c79ab": "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11", - "3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff": null, - "37657cf6ac04258413bdfdad4c38f0b2ad315f9fc73a4673bf3156090d4985a6": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83", - "3a709301f7eafe917c7a06e209b077a9f3942799fb24b913407674a4c1485893": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "3b9586d9ce9b99c2d02536da14607842db8e6bca846cd0fbd2163f0729f7a57f": null, - "3c486c3e0c4d802400904d0087a2c7f09fba492619d7195d3d2e1a2ce061d906": "08e2142845db159bd105879a109fe7a6f254ed3ddae0e9cd8a2aeae05e5f647b221108ee615499d2e0a1113ca1a858a34e055f9da2d30e6e6ab392b049944a92", - "3cffec46bcb0028c6a8dabf90d8c94d0742bccf673b37daf50b5f2fefa5325f0": "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", - "3d10be7e19bb353ee63548ba1ac13a3ddaa5b776190922e066dfa54b2075bbd9": null, - "43e0f2c14b3a7e08fbe006fb3d772d75a1c40332c5e1be232a3d72a324bc0959": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "451815ff7eabba9d1077296930b8d2eef8474b8f6821faf8b08d425753442f2e": null, - "45b0fe00f364a1c64f22644b69b1a3acaa12860cce89fcd5c5818190e0ddccce": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451", - "46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "46edebb0955a1b0736928529445b915f400a46836693b7a4d29f8964d5f54896": null, - "489c8a9cebfada630e213007ccb8f47d05c192a32e495ad8e2c9f52f4984aa70": null, - "4aa74a2542ef5cba3ef290a9bf4a92ca6d5e59c7ed9cdfeb7a0bfb8e6a65e711": "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36", - "4bdea052fc25a78b298c9578c906d6d19467c07088504e8f600687e266654c85": null, - "51e2d397029fb80fb918f374e62a247e0b0f0911fa38054e5be437dab97054ad": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "5309245443ea64938839abc0dcf3546927b25daa77e68726b086d2500875b2a6": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451", - "556a3c03566b04196c534f5612f50167917d72e6ab9b687e10e72dbe0e0f9279": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "57cd68ae4aacc9bacc38f018545889d3bfb788ee2f6d1602b98ba6a87bd69c53": "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a", - "59a9b598fb1cbb4470c1c83c3c7919723927e32dcbfc499369618883f7716c8d": null, - "5b164a05417e4774b9d16930536a24dab259131327bf3c08cb1e0397cc957502": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", - "5b2ab17cba6c53a260973bb4e4e34a8dae1c10852aae4c99be6b28e6f4135ba9": "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4", - "5cddef8a89d69efebe5b0cf235acdc23845d0b2b9fce36a4555aa746b06b2f10": null, - "5e993bee9fa8550de2393470ef41c461290fd3b6a0bd5b45335ca4d03208f9ee": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "5e9dc281ce55982d26320058f31ce9729dbc7eb9428b0426c17cbd5bd2683a88": "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45", - "5fc4b734667abc00b7a3506badc3045b05715a094356db5d3b438cd994500b72": null, - "6453c887cfcdcd148a4a5c4300ca4037a594efa72d8f5fce5b8f34cb2d4637ab": null, - "665ad019e44487bdfaa2bce09e1c568df406114d03e35ef8eb189f1e5858ac3d": null, - "678f7e9c5b1135902f5d18a8254d83f556a0faf5ff1da5d19a4e819ed0dcc802": "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f", - "6b8a720dde0076627771561a266a8f85ff6ffc1a69fc876345912f9c87df5041": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "6ca42bc823ca8545a34266e11d95a05708c8b18aadb419ba7e76379ff0dceec5": null, - "6cf5dca6ca4ea8f2b986c0515e09c3b4eb4b4379a92dfefaa23e47b84531a733": null, - "6e0c627900b24bd432fe7b1f713f1b0744091a646a9fe4a65a18dfed21f2949c": "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf02ab799bee0489429554fdb7c8d086475319e63b40b9c5b57cdf1ff3dd9fe2261", - "6ea4b3555d9ed649a46eb7f0186eb8050be3c2c91ab25930578229e441f60aa3": null, - "70dc3e6a46e2cde4c3f7ee504130e8d0d92fef898e9de79ae7933f992b283e62": null, - "774fc34071b0a8bf409509d2e89df6d52770db9b49d9fbf0f912f16ac1b7ca7e": null, - "77bbf777c3172667bf6d26ffcd5d8ffa381ba3ac6c3845e3cbee3cd1999bd5ce": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "7906e07848bcd4fce33a66745e2ebc6f8232204d4612408ec82f78dc8ef89528": "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97", - "7ca924efc0694161c95cdefc7b665a34b6dfea5521071498c317109fa1f83d18": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "7e66ffa2afc0ec56465d8595595426d226b6af122c7e53f6c91d8473af8158be": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "8188cc60754d8af0651dc0a4e6540b49021a9ca352aac2b6b952c55075536dce": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "848fed49db7023cfb8e9b80465e2a90687303544b4b8d76c9b216ea1ef6488f8": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "849d080a9600f5889060d925734849d28f9a75ed695b7e7a95ec4f741d6da0d6": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "85abc1ede88e37fe9e8467ff2a8b16c70504ebfb165ab45d3bc7cdcf183f4c17": null, - "8a1a74fe494abe17daba9d26942daf6196d747d6bc52b0ae4a0d79e9b5fba81d": "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db8", - "8b3165c1e8b0d9f7e843e277e0eaea0e3667bf135a13818dfd84d39806bdd376": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "8d5a589f63eb5b97aa05b53b2e64529e10298820205ab40196bd1d72b1bc0247": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "8db5e143a31c820e3ea0c08d690f03fd5d0442a8da1b4fe7d4e00f61bdd60335": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "9309cbcbb23fe4d31047c7d548000b159a2c464dcd7b17d8126222ad66f224e2": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "9fc428188049fd0fa72a1e1a8191082eb8a0466052e59e931fa17857a226603d": null, - "a123bcc05164c81bef25c915caa7df64e1e9def5cbc86582b99439ddaaffd9ae": null, - "a15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c": null, - "a555931c728f50406b843eecdaaa7a9e923dd188e106d1b147b917e97f1ddaf0": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "a7819cf665277e23c241e0c7c1fdec6106b5f841fdcd7ea3b1de8b2801fa9974": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "a8937aef5563fdf1d2871afb9f364e13efb42d4ace277bbd68be40629c0761cc": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "ac59ef256e50e52e22162a9e54cc49cc5245331cf529f2d5b6479ef30e9c77d9": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", - "ad2a1988218a943189518e3baafef7bcda90fce3e60154075bc46802f33b1427": null, - "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "afe1e714d2cd3ed5b0fa0a04ee95cd564b955ab8661c5665588758b48b66e263": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "b1a4e03e9a4f76ba8e6e7c2f205af4a34dff8f68c71b237ba3dcc438ec17b317": null, - "b427b100769d5222596f8a7c1b745d2cec4d0d583fce0796b539c7ff4cbbae37": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "b600f7004b9e43970a1846957ea322cf941537922388d8414c62d5629c899ddd": null, - "b6bd9a038d72ec0412abf486dd430137f2f593d08e7396907b54e37d7e528c02": "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf005acb4b400e90c0063006a39f478f3e865e306dd5cd56f356e2e8cd8fe7edae6", - "bac966e67df58e0b4c67757929a7d5fd4737d3609cfc6057c14c00ff6fb484f8": null, - "bb03b17d32fd2dd007abe32c004b5ad38f5a88060052c1593220a54e804b80d4": "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "bc6dcb9fb4f151eea79e0aa200dd04821cc7a3c55c04cc2f3b33692033938fbf": "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1eed5d5325c31fc89dd541a13d7f63b981fae8d4bf78a6b08a38a601fcfea97b", - "be18169fb07d71b4799477f51710506842ebc4f8947b6cacf10f1faaf0a9756b": "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad", - "be6098f8087523d5b42b98e8c871b437c3f134bfb42f8bf9af5b139d73d74ef1": "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11", - "bf528ce5e535e2cf241dc944b153d5925ab2cdd488c386e326716b6a8ecf751a": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", - "c1134ccf004f7f11c1a6815eaba3a6572cec0fa837e8295d1996298c763cbad0": "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b069610f239e3c41640045a90b6e1988d4ede443735aad701aa1a7fc644dc15a0", - "c4e43c7ad315c386f53d3fb72b9fa18ba208fdf4f513e74793035116c33ab28a": "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0", - "c52fdf54073f8252d616136e7c3baab0db60fe1b1966a9dfbd44a858f519904c": null, - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "c67f3dbb9c6aa6988930824dcc90b208f71e9b93e606b001ee4d99f5f8f7c8f1": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "c803b730a6e3116b333938ae3b7fd7b248dcba98b31aa47e139888ec198fe6b7": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "cbfe4baa920060fc34aa65135b74b83fa81df36f6e21d90c8301c8810d2c89d9": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "cc5c9c0d192affb052b231bb723b5e8cbb2a4394a2752554593744063ee2326d": "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575", - "cd65f545e45bb323a3a62159ab87c32e2c07be9c0dc545422943b7a72d2a814e": null, - "d0a30b63e7364b9a997d9d91bc1752f2136b872ebd0726a8fef883208d94144e": null, - "d15407cfe9992648c023fed365807007438478fc6dbd88f467f2448accca05b3": "2c15ed1902e189486ab6b625aa982510aef6246b21a1e1bcea382da4d735e8ba2e54101a155ea5a936da1173d63a95f2fc0118a7b82806f8af930f08c4e09f08", - "d499eabc9d44b6ed430fbbd909f6d2d4fd556304b8e3796834c078d54998c366": "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b29ce3d80a74ddc13784beb25ca9fbfd048a3265a32c6f38b92060c5093a0e7a7", - "d7bb278c9e616d23822caf15b26fd271f9ab8b8f0207e6053267518afc65baee": "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36", - "d7f62b8c5399336828fd4d5a0620cdbaf207e616f11053406af6ca1b1b04609d": "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", - "da4f3012b62992b1b1fd8c8eabc19b0d516d879177d3bdc33644b5c17bfd50e9": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": null, - "daa77426c30c02a43d9fba4e841a6556c524d47030762eb14dc4af897e605d9b": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "db579b30808cb2aa2bcee828f672341884413d2cecd9c836ac754965b99f67e4": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "db7faaa8166d502b98ae135bd9241263b5f40474a117706ddbe87a575448fcab": "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", - "dfc7054f7e556cdc2d4cbcede032e6e55d7f7f2f1e0d9cc5c2429cc32806b2ba": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "e3f7b4a842122149e1a3f1e969cb61d6e0e01750a40a7c8d7488dc25512293c5": "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1176f11fbb6e80611a7b04154401f4a4158681bca8f923dcb1e7e614db7e53cc", - "e4ec95a37af53bd0e28428aa169351d5b3daaa97c7cdc117b83aa948f3684e83": null, - "e5a3b3b10436c79bd4f0097b3cce534d6ade0be3d26072cefaff77db724d3b7e": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", - "e5b7541fc4ab4e82445c6549e3b5fe580b0cd1c066cbdddee4afcf37153c5853": "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36", - "e90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "ead59a785823331c690355fce6d486e5af45c04a3a44c3052defaaca4c30ea95": "03d64e49ebb3c56c99e0769c1833879c9b86ead23945e1e7477cbd057e961c500d6840b39f8c2fefe0eced3e7d210b830f50831e756f1cc9039af65dc292e6d0", - "ecc2cd90dd72f4f45f84575c0dc253f0e61c5db4635c4ef972b6f0555d65e0ac": "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4", - "ee37af4cb72275b346592f512af478612dadcc3e927993ff125d9e9a3cb62d8a": null, - "ee6733774a983e1e6921ab7c09c04f466c1b9748b54b849bfc96cb3ac08bad53": null, - "f2b53097de121a0fb6352179e7de2078ad0d88f3150e0b564a8bed30b5bb12eb": "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4", - "f947201a1362b943e410452f0c0014443d11ddba664d237cafe0d2a9f1413c9e": null, - "fa58fbc6150e7cec624278debd2b857592af7a4fcc66cc8d7fa82152c51cfd30": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "fd4e1783f7f6294d2ff12ac9015b97ab20feee1a465b28abd1701a3923b74381": null, - "fd5546c2496cb4df4b5e4bb86604ee8e8f36ad92844251227e89d9b07bb542ec": null, - "fdd792837fdc39009c8aa21f72e64fc95df92f3246b4385713f28c833383937f": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4" - }, - { - "006b0484c653b1be16a359057269baa24e343db52e44dce7c6cefeef149735f3": "0000000000000000000000000000000000000000000000000000000000000001", - "012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d": null, - "0352781082b63358a3b608d0a3797a054de3f008afe2f66c0e9ef8e6c95f4fee": null, - "086b338f7e1848d3b4403b1c68ec8aca7a528ede6b8ec2fe224befd44be88fce": "0000000000000000000000000000000000000000000000000000000000000000", - "1c99829c90fc052bddb247280db7e125d01ea5fa3837be302bb4385faf04c21e": "0000000000000000000000000000000000000000000000000000000000000000", - "1d6f02e093f23931ba8ca6054ade0f38e275e444f0f4e27e3a0a1a78ccab2a09": null, - "2549e1c511f9844e1db34c08642177686d490c699474dc995b3ae263ae3b246d": null, - "2fe4971f59c3dedb114db9798d3bdd9141ff8396e083e72ac3ce6cfedcf4e0b0": "0000000000000000000000000000000000000000000000000000000000000001", - "34ab9af661244dbcf8f5579970f5052c7d3c210ce13b6ca1c11172c7863a10f0": "0000000000000000000000000000000000000000000000000000000000000001", - "40653f9463fc27af1ae2d102513f34a73d3b3b7ddcecdd5b610ff03edeaf72c9": "0000000000000000000000000000000000000000000000000000000000000000", - "47b9c702be76fe4a97bf77b482a6b6fdcf92a7625c6a8d66be63659ced591999": null, - "5101633c6b687ee8aee29d9618ac1397a30cd7a14ff339cb19608101ed74813c": "0000000000000000000000000000000000000000000000000000000000000001", - "58272cd6bf0816a2a7bcc15804d2e94efb1dc38505d3349b6372f0bbcf5e1a6c": "0000000000000000000000000000000000000000000000000000000000000001", - "5a815339f8a25c8e84000b0372b4cbb4dd70277eb1f53dba5f51a419673348be": "0000000000000000000000000000000000000000000000000000000000000000", - "5e8c3b1154f324f528a87fd8ee700753aa61df9ac087029cca81909985a16ccd": "0000000000000000000000000000000000000000000000000000000000000001", - "60774953ab2e5bbd1ec195cb7d76557b747f69d3fa18e4c3f0d30330ddb0b27e": null, - "722bf5c9235aae2151346ce1d19ef04b006aa7766268bf836377445092da5441": "0000000000000000000000000000000000000000000000000000000000000000", - "730792bc8e23a24142495d77d62d22713f03bd3171abbe79e41eed071b064347": "0000000000000000000000000000000000000000000000000000000000000000", - "74ca988ae7d6995f81f6c3a04c6f619c9567a95949d2f2e197bcf06fc1ca38e6": null, - "79b0f933a47d870f4605811e2bcb2ca804b5d0afbae398b2f1b8106b30afa6af": "0000000000000000000000000000000000000000000000000000000000000001", - "86400f22e06689a2968f0a4089bda8c60df97f463e7166b7b967c7e919edf0ff": "0000000000000000000000000000000000000000000000000000000000000001", - "86efd7f5227806a4fc5de316f59ff565242eeb4cffcb8023616ed52069762b86": null, - "8efdc5eb586e50d63299241d89edb4f3eda845e9200dec5bd1f9e309347b52e8": null, - "982485363eccb2a35b40cb7c5389901fe540e00bc4261bb3d856a9b49f11207f": "0000000000000000000000000000000000000000000000000000000000000001", - "9bb3e6d1c58fbd7a54ab877163f65f8d593038532266f669f965d93289ff5f21": null, - "9eb22b148582de60152b381545be446f8b394c876d1401d42cabd689b76472dd": null, - "a60b9fc20ee07592f3581aca8ac1f77e609a1b353b3a66a6f4c29f521e533b73": "0000000000000000000000000000000000000000000000000000000000000001", - "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": null, - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470": "0000000000000000000000000000000000000000000000000000000000000001", - "d0c775f8d408d1c6e863c2d529a7edfbe3df1737d86bb6b69edc7508ad4e4926": "0000000000000000000000000000000000000000000000000000000000000001", - "d3eb0c32becbfb2d9c6c15edf0d51f84daac9dda3ce2d6506e1d4573db29bb89": "0000000000000000000000000000000000000000000000000000000000000001", - "d41981a62a65bc8d3ec7e86aeaadf0a5069f58c61705f64ead9d6a33f56f6b5b": "0000000000000000000000000000000000000000000000000000000000000001", - "da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": null, - "e45e2aa1bb354e722458c3425e59c37bb6a544397e5b4e8a5814ec97283dbfc7": null - }, - { - "0537dab8bcfa337395fdfbd203d054ac25c08722e9416a0ac259b779a2c87721": "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", - "13d12ef157bef72bdae72d1981ac6bc16ab74176742f7fb689ecef08d3d6bf0d": "d82c6a670dc90af9d7f77644eacbeddfed91b760c65c927871784abceaab3f813759733a1736254fb1cfc515dbfee467930955af56e27ee435f836fc3e65969f", - "2699f65d0bde17d17b834840e823616c33fb78e1a63f31fb6613bd1b08b888b4": "68790ca7594dd6fc28f0a86b7ddce0a225a8ea8fc2637f910eb71f6e54d9f8fa3e6302691015f11b15b755076d316823e6ce2ee4dd4aef60efc9189f6bd21bfd", - "30e40ece3441a7de9a9451e74fab98b455c4823f2485b2373a43a8b6c928a9fb": null, - "44b96fb380480dfeed4c297b46250c1ecc1a09e3e2fc2db0c1fb62fbe396eae4": "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", - "47a0bef21f27e645f4fcee8531e8ee64c91472c3c49f6dc12ba19eca9ab49e05": "cf087b9b6ae026d358a13c577a04a4af4e65293be70b7cb2a7e00bd1317440af94b896bca7c282001aeb86cc307973096917897eadc97ed8eefb0d2b3c309a8b", - "5440bab497f09b1558b910e1f0df278ae9ac9fedc88daa3648115e004cddc8ce": "59c7c9896cdc9fda0a77bd41adba14bdd6cb47cbbd7c338482f5382d7be16ac44a2ddfe6833bf9a15737dd66469d2d0495d39a9cb3c8ed93152684d92a74f8bc", - "72834c36258c46436fcd43d43893fb32103ebd8827774c031a2e64decbe71d6a": "a2c1eb780a6e1249156fe0751e5d4687ea9357b0651c78df660ab004cb4773636298bbbc683e4a0261574b6d857a6a99e06b2eea50b16f86343d2625ff222b98", - "7b12d31724098d4879b481a4d462ebc685bda0f24252af010a62ccc6f4ffc60c": "bc5e888ed71b546da7b1506179bdd6c184a6410c40de33f9c3302074177978895dbe74144468aefe5c2afce693c62dbca99e5e076dd467fe90a41278b16d691e", - "7ded868868aebb7359c79ef488bed027b61633040eb7da66493e546245a4a662": "38dfb111db6c59c39c9bea26c8c872620d89dec22fd7da93c47d0708a3973f522858fd9c60fae53f366d7e2820040a8662b336de6d859764f20747acbb8999fe", - "87d33c8eabd067a14a94f02cb8ccfdb7ecff66aedbc0f0b13ae4ab0e06a5c4c7": "689419d2bf32b5a9901a2c733b9946727026a60d8773117eabb35f04a52cdcf1b8fb4473454cf03d46c36a10b3f784aae4dc80a24424960e66a8ad5a8c2bfb30", - "8dea8fe46ba1734457ae75ffdbb4ef76cc7dfccbb059a8eed9612f2369c2ea8f": "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", - "8ff18398e840c4041ffc0bcafac552cfb6c3440c67ce4075f3d370e227d14476": "7476f1f8e159b30b156b0e9fffbaee5badbb45abb488ea9cfa04f60d3a0964087535a649e438ba993f03cfc0d8d8676a792030a996b6a6fde5c29108b6bfb871", - "93e382390cb513261e11e03f772356de95ce13c4ff3d1d91e8db0bf8e10f516c": "afd469613835ad3c75a54cd3087160eb308f5d6cd2151f1490f51b182dc5d13016428bf21e474e2921023bbeec971429210a51f0b63741583b0153fe8f6c27b6", - "98e4e90f2de200ae941b184f513fe7cb2a07db782ef99db0552da0a88e6c08ce": "94e963dcaa85d33dad6c0043b6700f5e227a2d8bed804bd16970e64fa6f1e16307399239bcddf968612c8c9ba953d9b173575c31ef269c3a8721cb9bf1c02012", - "ac284f457255542cfce05626f14734707a56698403c663e598a9305ce509b665": "74097ae7b16ffd18c742aee5c55dc89d54b6f1a8a19e6139ccfb38afba56b6b02cc35c441c19c21194fefb6841e72202f7c9d05eb9c3cfd8f94c67aa77d473c1", - "af40afaebdf61faac67fca7412c81973e9b053a85195ebc80a6f10c6a4b5d233": "5d6ff04d5ebaee5687d634613ab21e9a7d36f782033c74f91d562669aaf9d592c86346cb2df390243a952834306b389e656876a67934e2c023bce4918a016d4e", - "b7c8e0e46ba3b6a9b345fd795b9e328c51f3eb535bc9d775517a965af6b72521": "f3e89a60ec4b0b1854744984e421d22b82f181bd4601fb9b1726b2662da61c29dff09e75814acb2639fd79e56616e55fc135f8476f0302b3dc8d44e082eb83a8", - "bed4c5ce99729a220c87ca0ef21acbbe1274b29855df4cccba2510fd58ae1353": "a65aadbf393aad57c4b06d6471134c5c01002c23dfe8290e115e024e05bc1bf1084d1651de54a83902ed582cb8f2ba381c69687cceaecea0cd8fe5529f86686e", - "de7f79eb16d7357b75271fd4aa93af1237cbdd2297894cdec0033c1efb33ab2a": "bc112be5618b20d24be64c9e1c6efd63fea38cc79d53692fad6568b16e953eb6128c1ec8ffaf9a2d69e3cb043d6e11e1c7afd48573311052b6e7ec0960371186", - "ded8b44cbafe26629d64d2019a702d6eea0d6bfe68e313beacfa7935a9433640": "7df6f69476a03ae29e944814846460b058d1762fffe77f938ea723d1033de0d5bb1f8234bd73afaf955622fa2cdde95594577a8d53191908eb69b316a53c985b", - "eea1b2d664c19b902ea74b63cc5166346e9ce4de1de41dd5b72dbc75709871a1": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b", - "f352cd3242546c7cb1ee7c4dbf7c4cba948d505150b17fae38bff05efb7018fb": "a8ef8236e5f48a74af375df15681d128457891c1cc4706f30747b2d40300b2f49d19f80fbd0945fd87736e1fc1ff10a80fd85a7aa5125154f3aaa3789ddff673", - "f6f043c5d533a3310a6ccbc5ec11199f7038a750a570ff5be9e715eb3614c0b1": "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189", - "fb59d1e38c2473ebc92cd9d9bded35e13a214ab589f11fb07ffc0703784eb33d": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b" - } -] diff --git a/test/state/precompiles_stubs.cpp b/test/state/precompiles_stubs.cpp new file mode 100644 index 00000000..74f3c54f --- /dev/null +++ b/test/state/precompiles_stubs.cpp @@ -0,0 +1,458 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "precompiles_stubs.hpp" +#include "../utils/utils.hpp" +#include "hash_utils.hpp" +#include +#include +#include + +namespace evmone::state +{ +using test::operator""_hex; + +namespace +{ +struct HashedInputStubs : std::unordered_map +{ + using unordered_map::unordered_map; + + ExecutionResult lookup(bytes_view input, uint8_t* output, size_t max_output_size) const + { + if (const auto it = find(keccak256(input)); it != end()) + { + if (it->second.size() > max_output_size) + return {EVMC_INTERNAL_ERROR, 0}; // invalid stub entry. + std::ranges::copy(it->second, output); + return {EVMC_SUCCESS, it->second.size()}; + } + return {EVMC_PRECOMPILE_FAILURE, 0}; // not found, treat as a failure. + } +}; +} // namespace + +ExecutionResult expmod_stub( + const uint8_t* input, size_t input_size, uint8_t* output, size_t max_output_size) noexcept +{ + static const HashedInputStubs stubs{ + {0x010d1c26efabac677596f64e2e4f8894606e3636370a8665756ebc184e745aba_bytes32, + "000000000001"_hex}, + {0x012893657d8eb2efad4de0a91bcd0e39ad9837745dec3ea923737ea803fc8e3d_bytes32, ""_hex}, + {0x033274602281965ae0382e28ea7584792e3d846b4ead2f1529e0d61a63afa9b9_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x034d54cdad5956fb28f9060287fad9073c1e87fb7c6d1709e00f362c9052aff7_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x03c85b4c0caf8657ff29b05743c33f9ffbb8df604c46cc86caa2f051e6a065a8_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x067b3e4d57e1fe2d23c71a95cdd86c8019a649e466d5fcf8e22cce66ceea36fc_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x08774b45b9263bbdd4f040b01c444d167f20686daf1d9dc0be2703ac5b79898f_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x0ea4f1fe8ab439ed65bf736c2d1e75244a52fec0e96cefadf09ea5d170956d52_bytes32, + "0e7de84a5bcf0e16fa56b80cbf55f39877229030e5a8af78a0a78e003cdb657b"_hex}, + {0x0f38b3a063df12614121ae2d0cd202213a5fa95e3536e0f82c3bbffb311f9152_bytes32, "02"_hex}, + {0x0ff064739dbad34456d55c4dafd7d33b7e0e7c63b18a1252ef9fd8bc88867234_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"_hex}, + {0x10eab4762daae8abab287dd86a83326a78f5bbb8641f38a6792819576c94e35a_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x12292042f822da16c3db8dc1fc978be48218e0fb997cfff35857e16abba41300_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x12993abd44b4858730eb18a794ee686dc20b54cfee0e75a1f2e345cf9568ecc1_bytes32, "01"_hex}, + {0x12fc00c16a89e602782828a84667390d4a14bf74c1fd16dab784c7ebae45de06_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x130508100dd1f8b1c72b7eb16f82ee3b8a6f3bcbc8461c9f02c06f14ead89d75_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x1403e9d046887460408ff2c1956a064def64c4bb221b7bdff146b097b3a282c5_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x1669beee2b904266bc5a6eef210d6869ccea5c9a25e173d940428ccfbd6310c4_bytes32, "0000"_hex}, + {0x180fd71813c81062f2cdf852bc03fb8e8b8b98d21436001e255dc7141cb299d7_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x18c5b4c6cf9fccb97b1cf7f966b2ca8e951365e13eaf63ec93d3fd4ad87fadb5_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x18ca0a599a7bcdfd7e487cafca8ab1dfb6ef18074841fc3da95b73479eee3270_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x19a1b3731620b60c6decd10c1019cd9d6fca4fe6025d6b7307827ea994dc81f8_bytes32, ""_hex}, + {0x1a949941ca6b99c817a4e16cefe478308222535d19f1437ae2fdd892cb8ac789_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x1d885466c9ed74249248ad01d298eb4f509d9102a849d767f893da1e78799a9d_bytes32, "0002"_hex}, + {0x1e2d85db0c7c63bcc2054bf2d3caaf5d61920c77bb16f10572e6115458b0c352_bytes32, "00"_hex}, + {0x1e6d8ff8f706b4b423086b9c725a43e4aa6ce504b790612fb0e6f738cafa5b00_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x1ea137b55c9c2a68d4dd701fc31c5a11a2fcb3f9cc4c2fe6b2bf968d250ece35_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x1fe8359bcf1145edd7e23e4c32df953ea82d5db7a0d840ebe1d436f73bf93c5d_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x216ff577ab4b6adc22902060367bba5d514a67cb53b6171bc62569e58ce1a8ce_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x21879cfde84f139c4c53262a49dff3c774b0e2949fce670e2f471e54f1ffbc23_bytes32, "00"_hex}, + {0x21915510d6f6b58738bc790d97df3881396743ef975eb2a4d7dd0cd4bc10bc4b_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x24137ff708807fbf454fa0f7c3b603cdcf414890396424962389c3f887be5c00_bytes32, ""_hex}, + {0x244fa8474b97c1fc5589e3372062373bc939e2c2c8d51733cb62dd1af06d93ae_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x251828ea0fa55eeff4920c784f29d02b3d462893b0a53b35da407951f670d4c3_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x251f23d45faa62fd4edee6c7ca9d0e522dd618b21ca79c0ececcf6b4ab1d418e_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x2559c03f8499faccfb5d86c5a9c6857b9882045939b54fcab26046debc586400_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x25612858eb4e5086b12e02d2fbb70b8b64bb55022fdd629ec789f2c84607688f_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x26d4791888c6a5a6dad93c613727e1025a5e629993ace97b6f60a168ba5b643b_bytes32, "3e"_hex}, + {0x274f35379f9c71d56060a980068aa27bbb6b0163485b566c98f23208d9afebe8_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x27611fee3bcae57830f4acede0e4ddc28d6b80307e4e3c24518ff8427870b1a2_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x2815ce44f31ea08706a5b434ba9241be790bb99a9a14f2abc58f44e4c7ed5258_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x2aa26dd05f29718306030f45826699e7cfa5cee2d7e693743f08ca1890c70471_bytes32, "00"_hex}, + {0x2bbbf3147d86f35e028afe730dbe8a309c343a5af574d686b4de6f0299f44b62_bytes32, "00"_hex}, + {0x2e2b0152d440cb2300acb1a66f126dc6a55d48429cd4d1c65a6b08b1d70faa1a_bytes32, "00"_hex}, + {0x2e7668b70ebbbf0427b7206105b43be56ed44866084aef806b455c4302bb09ed_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x2ebcffbe9bfd10639c61895d63dc4adbea44b1ef949f81ec56b684a553ac3325_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x2fa614a47cc9c85b69679204e1ef9ac927b19471ded492b8ece1e8e67262fb51_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x31d7d7f3d30e2f6915813b0eae348106f9c36033685f734d666df55950721e09_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"_hex}, + {0x35de80ebb4a020482ef20b08e76ccf3047a069603f0f298ca69d20d652e8d84b_bytes32, + "00000000000000000000000001"_hex}, + {0x39d0581989c293b89482bfa1e116cec4009a18aa00b89c5242b7fe5c3766ed7e_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x39e4706fe515d7364d5d89bca544e81f9ef187062620ac4e97b4c64e9dba668a_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x3b41faafc90d54978545a2f7f0a0fbc5710b148872dd6e1d6c2ec4c94fee3617_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x3b73107bd69d6198ed057833aac40d0dcdc99e568c5e4f2ff154688bbab5fb26_bytes32, + "000000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x3bf09109c39d56015c8905aa26907c8f8c509e4a54ff4efee2f896a8b1176042_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x3cc1cc331ba2e9897c734d97dfb178fecedbc4cc90c706eee0c6059c94b36297_bytes32, "0000"_hex}, + {0x3d8f5d715374acba3f92a425b8bad30eae5f2f534a63c4cea5ad4401fe46ad75_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x4084709b90b82b198ec95c387a53f65e5492611a56a9bf34a46d7c5fa02907dd_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x418b0655db32fa5820ed237053dfe34869c9a1c39460ed8d58e6e87e5164f8ec_bytes32, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x4262e4bfbfbc25391172acaa697e2b36c9aa60be165a81de92f9a6be0ed447ca_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x42c4e39cf1e2224199b3895508025d4cf89fc21154dd0ba0c9f86ebbe81667a1_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x42d09d5551133d4982c7c496aa710fdeb46c2974fa2ee405bd0cf2d8687f5eb4_bytes32, "0000"_hex}, + {0x46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21_bytes32, ""_hex}, + {0x469eae86b1701336b3d583df4527d639b1cccca716608b728563a0eeeeeb55ba_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x4798efaf9795653086f314b2a745a3c677befc3a143f8e71f6961410234d4aa7_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x48cbcce9cddcfc87a9dfb28f8c07a498c6cae91c3577410009e009de8ae15426_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x48d102df204080cb58f6131e8de27892647da0958ab111e3b7604279fe7638d9_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x49daba1e38b9fdb769584f02a5cf0d5ccc52e1dcc5f1683c384e308cbda1aefb_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x4a4e7e22f228bbfeef1e2e652aeb39630a3fea4545ce98694e34b3a13cbf4d9c_bytes32, + "162ead82cadefaeaf6e9283248fdf2f2845f6396f6f17c4d5a39f820b6f6b5f9"_hex}, + {0x4b1c0353029df7c0e24c97c1caa608b2447a736abd6ec92c6a6ad19c74af6764_bytes32, + "0000000000000000000000000000000000000000000000000000000000001c3b"_hex}, + {0x4c1451f2f475e314e13ab026083f0e75042da3757331579bcc2acf982eb93e47_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x4d26511722a244d920d5b388b5008248c71b7232df5e2dad0c68731ecea7baca_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x4e895772ea04e2bebc4494c0f61d649af0ec15eb35e3d6fb23ccd43cec8fc477_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x4e9bc8aacd818468cd54729198300a542eaa7ed24d172b0fa62eaca18c80320f_bytes32, "0000"_hex}, + {0x50f0b7d9f0b0195307199ecc68ea12ec8b2855ebd3938241acea7eab945706a3_bytes32, ""_hex}, + {0x560aefbbe4dc7910322f1a57808798dd21e6f20b6804497d76feb28109d9e4b4_bytes32, "00"_hex}, + {0x5627aa867eedac80321e8a99321c11d624a974907a00338d183e495737ad224b_bytes32, ""_hex}, + {0x56cd39d56e15e186eb742169736637742fe0129e3816caa42199e1d11444a087_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x57f24ba6197dd6bca4b92b2b069547c4d9715cb715b118fd06b6c506f3fe9a6f_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x58dca178acf545c5f569bf230c25f4b299df65d50427e5599c062de84ae8e117_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x59083a31c083633493c1f0fff98188fc553195939b97664ddf4f1f7372fdb9ed_bytes32, "00"_hex}, + {0x5913b435dbacbaf8a060c4901cb61a7aeea4f64e04bed5194a9cda0e3f7407b9_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x5ad3326c83fc81b119a19e072abec9b015d3230407d61fddeac53d8d622665d2_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x5d8c10bff855aeafad14cc8945d96363078f03b05fdd68cc8bf7db0f345f344d_bytes32, "0000"_hex}, + {0x5daa348868b1c7f027498f63593dc226890161a6e9fc6a458299fb2d954354d2_bytes32, + "0000000000000000000000000000000000000000000000000000000000003e80"_hex}, + {0x5ec71d7c8e69482d0e0118734635e881ff05f5a87604da2302f19e3ade457b42_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x5ef61126f34e679f7b6d859b4f20cb716481fe3e7003d0bcfb017694f6d28649_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x65a35fae82f3775f4e1b92c56cd68718784dd29d132d62722e7bda3853a3d837_bytes32, "04"_hex}, + {0x66b7f8ac5bd4017cc1ae82dc7a1a5334bcb127a1f64ebe4f1a7595196da10a58_bytes32, "0000"_hex}, + {0x67f7c6a923a7558c8cb23e8475dda653460be6ee507466645d79d5fa6dd5c63f_bytes32, "01"_hex}, + {0x68ad0981c9f951bdbcad78d21e35e5ec7918595b341c8b3dd81d5f50eca06b78_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x6b1061285efade3c0b2593286bb08db69ae28df9e1d6714041b063e362ba1988_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x6beafa77883efd2c221ea4cb29831d1fe10a2190379312217dbe57909ac356b0_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x6d835e80050760971242c410fa7ddaf8b268d3669e2db343fd61343cd87d80f4_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x6de36875921c76ab5434e64791cc62395d65f49eb21f9ddaa82aa8756b44ed3d_bytes32, + "000000000000000000000000000000000000000000000000000000000000866a"_hex}, + {0x6edab1ef76af731a34f9a306bfdeeaefe48aa8f4826c329e1fdd702652e17927_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x6f64f08deaeee1f2aa90289b4aaafc4d76bf5d79a6f0aee01cce6f914d6523af_bytes32, + "0000000000000000000000000000000000000000000000000000000000009c00"_hex}, + {0x7030a0dcea1e544f2f20cab453564c3f7343ea3c7949e30b35c9b562d2a6f532_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x72937ea411f48b90764786c97d4229d192cc6b1426fabdc86b59298816af97bc_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x73291d37d5bdc108723a04c171d113d6c323c46000569d0eaa319e295497a911_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x75137d2ac452fa56c54f3a84b6a5032cf7e9979578b76da3d8893684ffc3c0c7_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x75e5c2f08ba7f4f5196eeee2fa5d4585c1a7d13d94c8dd01d8db7c6664bf5dc0_bytes32, "0000"_hex}, + {0x7687fd0273528d2f1aa49723afefa95a867eaa5a6a898a75d8d1c1774f244cdb_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x77d6ede055e1bf3669db1b8e965fcced0a76864f364f6135e6cabe1037fe4809_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x7870eaca19c10f38ad151dabd337cbc8e2df484ac4801b07b0d80348b645931e_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x7bf0fbb8f1754a19c5efdf2dbdc4288c2d8728852734b30dc057d53beb59e5de_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x7d24c042d652db2f2d9e2e5d1eae97d19b44109420f11b6eadb6b90cb1ba50ea_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x7f2686134db3a520c34fdf758eba9a0d4f9c8945a3f06d1f4d7eb68b433be142_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x80de6c463032565ad835285aac5101f9748b93dde6ad45bb1136190bb8f5c253_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x8187355fc5e82694be920349dee28c7d4ec2a7bc15a85db4db530b13f34b4050_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x8191431cab44389a73030b5819d7af3bcdb3cb49e618810032cb1fd4e746bb41_bytes32, "0000"_hex}, + {0x8257bcfd8eb8eb906d8f3a0f3cb71577e6267f1a4af7f3fc611dc19dd96192bc_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x8507835cf8ffeccb9e5a2ef45b957ae5f463861884bcd279214fa36b2965619b_bytes32, + "0000000000000000000000000000000000000000000000000000000000001c3b"_hex}, + {0x86d444d512bd04db648bc23e024132fbc2186aa73cda0bf0741114132324124b_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x87b97726f9d1d6e04d170320fc2d8b16a971ecaa1048ac38dabab51369bd4543_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x880480a0dbe4cddcdb5cea5a9d483524b2f6c4e7b492bfe236b8d15649d0673d_bytes32, "00"_hex}, + {0x88451def3af9b5d137d0082f973d90facb971643f4eabf3c201a634861facef4_bytes32, + "000000000000000000000000000000000000000000000000000000000000001b"_hex}, + {0x8a068cd204782a5859890b4201f784e3ade964c45f373bc4e20f2f0c60c927f6_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x8ad16be55060aff4f690974f6b8c74de388599e798c8ff48261897a95729fb9f_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x8b4d99f7357e3cf1c7ad74cfce0c1bab1ab69920595b7bb2c20ed919aa03f77c_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x8bf994b1ef100d61c6b3857392be820ae7855cc194cfffa90d3b43d0a28b13b5_bytes32, + "3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"_hex}, + {0x8d69df480ceb95c657631fe973f8777f032a807fe36e698458813ae8ac5276e4_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x90ac1c964e1edcf937f9332f953f971919ef44a105ec663be0eb6707ef52de9b_bytes32, "0002"_hex}, + {0x91bbd8d35060a647704e6e8410263135b9df075665d0286495fba2ad2129d178_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x93d390c50ad9871ea4b3ec9de9c3f10646951691931e069e03c0de0675bc6b05_bytes32, "00"_hex}, + {0x96cf34086b42afae29477ebe8d6eeba6fef1da1690e0ea32d8c4947466324569_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x993d6040be7987f83bb56811395b7d76d577bd885b943d2755b44fda0b44cf50_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x996f9283a0477c8eaddbd50c4a9927c80b0281d3193538469f79ae95f8ec2f5e_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0x9ab09ec74fdfe6a0f390d51b70f6b31a9b99287080d91395864c689ec8ef96e1_bytes32, + "0000000000000000000000000000000000000000000000000000000000000050"_hex}, + {0x9c3a806764dc757a3c4f3e4ab27f2d645f13684a5d0f65b3e527fd144464d052_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0x9ce2281a0a9ead36edc758647abc060766f200a5210d5b97a9b7d1752578c3ee_bytes32, + "0000000000000000000000000000000000000000000000000000000000008615"_hex}, + {0x9e24cd89db1e412aa85e71e9ea955e00a1c22d66716bbd19fc92df1a122a71bf_bytes32, "01"_hex}, + {0x9e6a8ff68b83a4a01c2c344f507ab1c738cf5df1f6f846d47fea6f5d397b06e4_bytes32, + "000000000000000000000000000000000001"_hex}, + {0xa05b578a2942544883921581b74d7bc5f44de59ae5d1a3cc21626658b5d9cf41_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xa4ddac19e1b86ed2a29ecc5ca2e2d840923f30380f5c2a732b746427b97c7970_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xa923d0e400cbbe1e0915c5a699cb8a51578ae87755f1aae7929289166ff23f33_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xab76f1d5488b8542336a386818cf99412b7aa17c589d244c29f9ae9cb3901815_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xada673786089f911b5dc7c5fc3405c206e6e7564e4dd4466b563d03e659e94a2_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xb07a3d24561ccfb56dd51bed4d640a0f00d5d86fe953d986846be0930c35157d_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xb0e9dd0b5749795680b0cc0db0167b85f1d2417acf9b44869d17a876a4f422ba_bytes32, "02"_hex}, + {0xb2f231521511b65fba5f194f65a93426b16df90c4ca4bbf0b51e2c3b02d59b2b_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xb303e361c4edd99c9e86513e769056c2faf0359ed8ec911ab473f14d5e8bfe95_bytes32, "0001"_hex}, + {0xb50e0123a517ebb42db0e4d926c86a515d5cc55cbe6cebe6b6a99ecd3a0dcd90_bytes32, "01"_hex}, + {0xb5a34ff06af3b01441b6ee4ffac60feee6f0b63ed6be80a21c8aaf8b9e7f0b4d_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xb7c17933a8cb769998187b25ea82c3ac8dffcf3d1976a60b023b9d06c0cd9a4f_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xb97681ddced4ee8f4efffc5b58044de3100b2dc7c1f7f6b72161200f10b0604c_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xb9d52ce810f7c808324c717f1a5b612512a24a3227d311f508edec3c931344ad_bytes32, + "000000000000000000000000000000000000000000000000000000000000005f"_hex}, + {0xbafcbd8a4bbc5837e3a637e432c4ee0a48e30a6b760efaaed699aed606b0c768_bytes32, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xbc2ae76808e3de30404a4dd4221f3289cf20a272161d4f8c0c58c2e736706256_bytes32, + "3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"_hex}, + {0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a_bytes32, ""_hex}, + {0xbc8a5df0db3fc6a076ea676a8dd5f6689777de551a235d75f77a520bd243208d_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xbd59618a09cdeb7538ce8cf97820ae2ab18102ac903c34c7de6d72c63c3d0f08_bytes32, "00"_hex}, + {0xbd66e927d01f078a67ba5c98539a6a2f79b7187457430237f7d406ff12558deb_bytes32, ""_hex}, + {0xbe17aa6916c3d516ea45c2b8fc312f6bffc47f14325b7a9005786f878e1bb3ba_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xbf528ce5e535e2cf241dc944b153d5925ab2cdd488c386e326716b6a8ecf751a_bytes32, "0000"_hex}, + {0xc1ec6ef309c736331efc3000afc186d2021944309ee6b20a4bf3bc735b7aea75_bytes32, + "0000000000000000000000000000000000000000000000000000000000006801"_hex}, + {0xc3e4a3530ca51ae69ee63be92701c84c6ebd820e1f89ca15548c94dbb851101e_bytes32, + "00000000000000000000000000000001"_hex}, + {0xc4527e59efc328e49b6926353042b1758cc422461230653e83a433634155adcd_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32, ""_hex}, + {0xca29ae8887f56b58494b8404be63728fb620247345837253ee264b09b9c1057a_bytes32, "01"_hex}, + {0xcba5ff6688618456c8228b8c2bfb4ed254ad8620b99ef3ed8fbcb5bc66feb478_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xcd223a9e187147d1712bdeed93889e2edad86091108b10bb97790428717dee31_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xce9b2bf13532af4013c467e57479a71625bf1a7c8190393f29ffe8d3f3275aae_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xd09419104ce1c64b6a06bcf063e98c2c91ad9e1beaf98b21c9d4734b4a3c9956_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xd6c0c03ec1f713b63be3d39b4fa8ef082b3407adc29baf74669fd2a574c638ac_bytes32, "01"_hex}, + {0xd837f9dcf93155fe558c02c7a660edc0cd238a8b8f95ee6b68e4a5c6a41fc70a_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xd9e1670598e5304eaf7da7b328570d20fe98b6977064f5b922c361d5c75751b9_bytes32, ""_hex}, + {0xdb5d4994d6dae845a6fd76e91ade4d3a35f270d86673ccc32405042c3c3d37ba_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xdbc6ddf9220c7ccfc94057fd3d25b712a0d7f51c3ff44e291458f2daf4ef2ec4_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xdf244f503af99f62276115256c481d3cf0786d477553cf0bb25df1579dbc9ee3_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xdfab0a2d13154e415f4f999b6570efd185de14a2cf07a14eb543feef7542a5fa_bytes32, + "000000000000000000000001"_hex}, + {0xe0aed3305cfbf6b99096f7ea895c0f2d721dea396d08c75c892152e757668eb8_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xe1223738398f40c909a2f63d7f617ea86f1c6b8bd82786ec6e3ff0a8729ee803_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xe1d19f16b8648afb5fd13c5bd437554c9d1bd61855b47cf4069d6068f27f90c5_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xe6f0c93cd48787b5c65c1df991ab038e7fd1512ae6fa680ca79051348e4bd2c5_bytes32, "2b"_hex}, + {0xe7e08425a2da0d12f7d6cdefc8722e15f577c8b7908924ef5a6f09dc8a8df381_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xe9321ede4409baf4d20ce11283a0267a934062cd546b6fb0097d755686ff0194_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xe99b55f3b486798837a08b10ad2cd917ab907b7c3d62ab7cc31f0d9aeb19c043_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xe9c48c3e8bcc466c06599e85e1209d23de86ec441fe5ec9b1042a6275f2c818c_bytes32, ""_hex}, + {0xebd3e19945cff9b7462359bb882a8005d9d1d69d9f82db099f29508447ec7b59_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xece1937784110ebc937ef343c108c6385a8b13b3e8d0617d30bb6bd95701f270_bytes32, "01"_hex}, + {0xefdc1fd8d4c7bc0df6a2dcd82e30e65d2964daf02b52c51067c3d2e525943f18_bytes32, ""_hex}, + {0xf0001b79c0ca5c8fa824f187c3e987fd066688f64c1763648df46d59bfcd7a32_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xf109ca1998eda403604fe597551159daf3bf1c60b6a9f28b02c66fea4594fa33_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xf206ef1f61673596e4abc05dca7be9148dbd34841d85584e8c41d75ecdf5a936_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xf344c28b47c6de556d35ec1e82c6ec3d06f1951d36aa1cd7351d4255e7f04a9c_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xf3f136516fd855c4cd4bd193c2bc38fab112196b023eb22510da3c6cc276eb72_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xf402d2cd2eaaede36f70e970a0e397dcf76e4488dd6304bcbc77f8ba8bc423d0_bytes32, ""_hex}, + {0xf6183b77357ae6ff58dc23d48b3f1d9e76098e6f884751ed03a261a0e7bab231_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xf77262efc56997fdcd4a91cc1449b01f93ebca8c758efcc1c85027e69cc6618b_bytes32, + "cd433bbd1fa6457602a79d957ee85a37e2496d0a"_hex}, + {0xf9adfd03d4d3cf7e9245f9677b882459f0f0ea492b36ebbe34fe965bf3b4e05c_bytes32, + "000000000000000000000000000000000000000000000000000000000000002b"_hex}, + {0xfa22beaea48d8a5e37b2f00efdc83600d4a9d1e1ff5a08cea987db6e6d168833_bytes32, + "0000000000000000000000000000000000000000000000000000000000000001"_hex}, + {0xfaccb2379850753cd96f3a4f37dd3f431863cab480cd1f808f696b0cafab5da4_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xfb859356d00b38d191345159ca428ce6b47c7c03000e91d93ba541f2db07ee75_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xfc08a758057657032bc36068292a2e3dfa490f21697d4e5841ce2bcbf50b12ba_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xfc94ce88596dbda4103ce1ee25fe0f05e0d8e1a57a2d8660d45800ef89ea2f64_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + {0xfe0d647b508fe5dbc76db4136a295fcc960ec1990c7639bb8ddcd06c1761e9e5_bytes32, + "0000000000000000000000000000000000000000000000000000000000000000"_hex}, + }; + return stubs.lookup({input, input_size}, output, max_output_size); +} + +ExecutionResult ecpairing_stub( + const uint8_t* input, size_t input_size, uint8_t* output, size_t max_output_size) noexcept +{ + static const auto _0 = "0000000000000000000000000000000000000000000000000000000000000000"_hex; + static const auto _1 = "0000000000000000000000000000000000000000000000000000000000000001"_hex; + static const HashedInputStubs stubs{ + {0x006b0484c653b1be16a359057269baa24e343db52e44dce7c6cefeef149735f3_bytes32, _1}, + {0x086b338f7e1848d3b4403b1c68ec8aca7a528ede6b8ec2fe224befd44be88fce_bytes32, _0}, + {0x1c99829c90fc052bddb247280db7e125d01ea5fa3837be302bb4385faf04c21e_bytes32, _0}, + {0x2fe4971f59c3dedb114db9798d3bdd9141ff8396e083e72ac3ce6cfedcf4e0b0_bytes32, _1}, + {0x34ab9af661244dbcf8f5579970f5052c7d3c210ce13b6ca1c11172c7863a10f0_bytes32, _1}, + {0x40653f9463fc27af1ae2d102513f34a73d3b3b7ddcecdd5b610ff03edeaf72c9_bytes32, _0}, + {0x5101633c6b687ee8aee29d9618ac1397a30cd7a14ff339cb19608101ed74813c_bytes32, _1}, + {0x58272cd6bf0816a2a7bcc15804d2e94efb1dc38505d3349b6372f0bbcf5e1a6c_bytes32, _1}, + {0x5a815339f8a25c8e84000b0372b4cbb4dd70277eb1f53dba5f51a419673348be_bytes32, _0}, + {0x5e8c3b1154f324f528a87fd8ee700753aa61df9ac087029cca81909985a16ccd_bytes32, _1}, + {0x722bf5c9235aae2151346ce1d19ef04b006aa7766268bf836377445092da5441_bytes32, _0}, + {0x730792bc8e23a24142495d77d62d22713f03bd3171abbe79e41eed071b064347_bytes32, _0}, + {0x79b0f933a47d870f4605811e2bcb2ca804b5d0afbae398b2f1b8106b30afa6af_bytes32, _1}, + {0x86400f22e06689a2968f0a4089bda8c60df97f463e7166b7b967c7e919edf0ff_bytes32, _1}, + {0x982485363eccb2a35b40cb7c5389901fe540e00bc4261bb3d856a9b49f11207f_bytes32, _1}, + {0xa60b9fc20ee07592f3581aca8ac1f77e609a1b353b3a66a6f4c29f521e533b73_bytes32, _1}, + {0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32, _1}, + {0xd0c775f8d408d1c6e863c2d529a7edfbe3df1737d86bb6b69edc7508ad4e4926_bytes32, _1}, + {0xd3eb0c32becbfb2d9c6c15edf0d51f84daac9dda3ce2d6506e1d4573db29bb89_bytes32, _1}, + {0xd41981a62a65bc8d3ec7e86aeaadf0a5069f58c61705f64ead9d6a33f56f6b5b_bytes32, _1}, + + // https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stZeroKnowledge/ecpairing_inputsFiller.yml#L88-L92 + {0xedf5a91b7972b77cef24e42ebf683ef81bdbc2f97cad1e6197f42cdea5e9d489_bytes32, _0}, + {0x38052a6ac4c5131f3391bb49ad3f1d5f00c9eff3a68696eccaf776c31de66755_bytes32, _0}, + {0x4bff5699271962bf77165efea69c77ae9620d5d63d9e6cf2aacfa6180d76be64_bytes32, _0}, + {0xb4e6d4451f859e560863f3097450b57c481ac2604889f528ae25fd82294bb2ee_bytes32, _0}, + {0x2c6a9ba3568c7c3a4d167645185dc247e56fee419984c63071ad398315c91e10_bytes32, _0}, + + // https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stZeroKnowledge/ecpairing_inputsFiller.yml#L93-L125 + {0xb67d886462fac350fdee4f6c7e9039e76ecd8b76bb3966da171a3e27667c1ec9_bytes32, _1}, + {0x1335e5982cf4b93dd6841c0037bdebe204b87e82e10a3c79c01f762a1e7c4d1a_bytes32, _1}, + {0x954e14906cb50e29de9f03d4a36c64f5404022853d22bdceae6fb4283f8c1832_bytes32, _1}, + {0xe5b53f3cf626793a6025fdd553f7b541ac3cb92dfb93601902e87b2827eff7a6_bytes32, _1}, + {0x34d0e58d51512383475806ca14f4a5c78459060fd01cd4be57ff9f7c69a6351b_bytes32, _1}, + {0xd59908760c797c4e1abb89ac297c3584c0fc0f56ec5c4edd807813fc3104143c_bytes32, _1}, + {0x38fccf0bf46a22541e77dbe7f8349b5ee76dc074ef550c3b0c79919f102a3d7c_bytes32, _1}, + {0xff13ad7025ffe82f4f3d05bd7981640865cbe06f8ad8f10305410c57e57c853f_bytes32, _1}, + {0x3567df81192e58f480ee7424c01bef8ecd7cb831f30c3550977acda477c7d457_bytes32, _1}, + {0x4e399cd983cb3849d73c8132b3af7700fe8eb76aa1ea359462abea6d6b385341_bytes32, _1}, + {0x4d52c96da659d1fba37d0b5658afc3ba1d2c593753e01dc37be19c3f723f6ffb_bytes32, _1}, + {0x271eea9d94639a22eae52eee98aee895210f60539d20bc4dec1949e461107395_bytes32, _1}, + {0x06a5d258a138e0430e5fa9e5d3d0456d2081ee35014d06a44559120272b04927_bytes32, _1}, + {0xf1c5b77850ef1d521ded25b7b6940b54b47601c28b46bc512b22721d953a8f45_bytes32, _1}, + {0x76d73dac407ca9a4aa671c25628f3d9ca60bd7490c42f27201c5e5214b726353_bytes32, _1}, + {0x642ce9262387f9368e917753262ee445c20c27334e9808b924c8ef00e12fcb75_bytes32, _1}, + {0x7407aa98cf3a0263df6d1c3f5d24a0ffe29bd8cebba48552a50cb06b4fb225ad_bytes32, _1}, + {0x50847a4741f81644bf49a4ce22329d8487c34897001701b35cf1fcb37cd0590e_bytes32, _1}, + {0x73cfc82608f54bd4e6c7d729afe9e690adf9d88e856f0aa6cac2b26fc7890ef2_bytes32, _1}, + {0x0676ac886e215342147e4ff6f2d62a3d2a79b41ba532760394098840efb5b11a_bytes32, _1}, + {0x46958a07ceea64e3e46b9338b85718027ddca6b6531ad59912ad98fc30bdfb74_bytes32, _1}, + {0x29780bc45a549776559b934dbb1ad289a45e5d0b07c7a75401ac311a99d4bf8e_bytes32, _1}, + {0x4f519b293c0fc788e39591955bed841763afa836b07afaba0dbf28db756122e8_bytes32, _1}, + {0x59c33dc069ff2818c161df2baafe51a21106739aa4f54e4251536833aa0d28cc_bytes32, _1}, + {0xede6d3ebd333189a99f4d5b6ca6dd03296f7b3c83dba9ebe5a99a8eecac126d3_bytes32, _1}, + {0x75aef1dc2eb432680d33ed9d330008dce5cf71e9e3cd6bf4b68575e5e613a076_bytes32, _1}, + {0x151397d397dd7647321e386990a1fcbb13544b507ffb29c38ec95f10f9ea65d5_bytes32, _1}, + {0x3ef93cb30d7b786f9d294566676aecd29dd7cd05ee1da48a3f8281f7cfd5923c_bytes32, _1}, + {0x45f50343a48f7ea57bfc317e5170b945e748124cdbedacf163fe0674790149ad_bytes32, _1}, + {0xbfa15492c7462ec9ac2a2c4a1f02ef12518746920ce5254673a9af0917f5b856_bytes32, _1}, + {0x06ea20597bf9cf2951d7a127cbbac029ab479d668e8cb848595fb8af0d2620d0_bytes32, _1}, + {0x14fe90e9791ada31b07cc85195109e573f1902fa2692a12113ae0a1af8c9753f_bytes32, _1}, + {0x7ac98487a7b592bff7e5b04f9e5454f218ebcf1b51c705794b795860a35f8253_bytes32, _1}, + }; + return stubs.lookup({input, input_size}, output, max_output_size); +} +} // namespace evmone::state diff --git a/test/state/precompiles_stubs.hpp b/test/state/precompiles_stubs.hpp new file mode 100644 index 00000000..8c16d963 --- /dev/null +++ b/test/state/precompiles_stubs.hpp @@ -0,0 +1,14 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "precompiles_internal.hpp" + +namespace evmone::state +{ +ExecutionResult expmod_stub( + const uint8_t* input, size_t input_size, uint8_t* output, size_t max_output_size) noexcept; +ExecutionResult ecpairing_stub( + const uint8_t* input, size_t input_size, uint8_t* output, size_t max_output_size) noexcept; +} // namespace evmone::state diff --git a/test/state/rlp.hpp b/test/state/rlp.hpp index c288c992..3d54f4d8 100644 --- a/test/state/rlp.hpp +++ b/test/state/rlp.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -13,15 +14,15 @@ namespace evmone::rlp { -using bytes = std::basic_string; -using bytes_view = std::basic_string_view; +using evmc::bytes; +using evmc::bytes_view; namespace internal { template inline bytes encode_length(size_t l) { - static constexpr auto short_cutoff = 55; + static constexpr uint8_t short_cutoff = 55; static_assert(ShortBase + short_cutoff <= 0xff); assert(l <= 0xffffff); diff --git a/test/state/state.cpp b/test/state/state.cpp index c8d646ed..aa74c4c3 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -3,11 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 #include "state.hpp" +#include "../utils/stdx/utility.hpp" #include "errors.hpp" #include "host.hpp" #include "rlp.hpp" -#include +#include +#include #include +#include namespace evmone::state { @@ -44,7 +47,7 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex static constexpr auto call_tx_cost = 21000; static constexpr auto create_tx_cost = 53000; static constexpr auto initcode_word_cost = 2; - const auto is_create = !tx.to.has_value(); + const auto is_create = !tx.to.has_value(); // Covers also EOF creation txs. const auto initcode_cost = is_create && rev >= EVMC_SHANGHAI ? initcode_word_cost * num_words(tx.data.size()) : 0; const auto tx_cost = is_create && rev >= EVMC_HOMESTEAD ? create_tx_cost : call_tx_cost; @@ -52,99 +55,356 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex initcode_cost; } +evmc_message build_message( + const Transaction& tx, int64_t execution_gas_limit, evmc_revision rev) noexcept +{ + const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{}; + + const auto is_legacy_eof_create = + rev >= EVMC_PRAGUE && !tx.to.has_value() && is_eof_container(tx.data); + + return {.kind = is_legacy_eof_create ? EVMC_EOFCREATE : + tx.to.has_value() ? EVMC_CALL : + EVMC_CREATE, + .flags = 0, + .depth = 0, + .gas = execution_gas_limit, + .recipient = recipient, + .sender = tx.sender, + .input_data = tx.data.data(), + .input_size = tx.data.size(), + .value = intx::be::store(tx.value), + .create2_salt = {}, + .code_address = recipient, + .code = nullptr, + .code_size = 0}; +} +} // namespace + +Account& State::insert(const address& addr, Account account) +{ + const auto r = m_accounts.insert({addr, std::move(account)}); + assert(r.second); + return r.first->second; +} + +Account* State::find(const address& addr) noexcept +{ + const auto it = m_accounts.find(addr); + if (it != m_accounts.end()) + return &it->second; + return nullptr; +} + +Account& State::get(const address& addr) noexcept +{ + auto acc = find(addr); + assert(acc != nullptr); + return *acc; +} + +Account& State::get_or_insert(const address& addr, Account account) +{ + if (const auto acc = find(addr); acc != nullptr) + return *acc; + return insert(addr, std::move(account)); +} + +Account& State::touch(const address& addr) +{ + auto& acc = get_or_insert(addr, {.erase_if_empty = true}); + if (!acc.erase_if_empty && acc.is_empty()) + { + acc.erase_if_empty = true; + m_journal.emplace_back(JournalTouched{addr}); + } + return acc; +} + +void State::journal_balance_change(const address& addr, const intx::uint256& prev_balance) +{ + m_journal.emplace_back(JournalBalanceChange{{addr}, prev_balance}); +} + +void State::journal_storage_change( + const address& addr, const bytes32& key, const StorageValue& value) +{ + m_journal.emplace_back(JournalStorageChange{{addr}, key, value.current, value.access_status}); +} + +void State::journal_transient_storage_change( + const address& addr, const bytes32& key, const bytes32& value) +{ + m_journal.emplace_back(JournalTransientStorageChange{{addr}, key, value}); +} + +void State::journal_bump_nonce(const address& addr) +{ + m_journal.emplace_back(JournalNonceBump{addr}); +} + +void State::journal_create(const address& addr, bool existed) +{ + m_journal.emplace_back(JournalCreate{{addr}, existed}); +} + +void State::journal_destruct(const address& addr) +{ + m_journal.emplace_back(JournalDestruct{addr}); +} + +void State::journal_access_account(const address& addr) +{ + m_journal.emplace_back(JournalAccessAccount{addr}); +} + +void State::rollback(size_t checkpoint) +{ + while (m_journal.size() != checkpoint) + { + std::visit( + [this](const auto& e) { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + get(e.addr).nonce -= 1; + } + else if constexpr (std::is_same_v) + { + get(e.addr).erase_if_empty = false; + } + else if constexpr (std::is_same_v) + { + get(e.addr).destructed = false; + } + else if constexpr (std::is_same_v) + { + get(e.addr).access_status = EVMC_ACCESS_COLD; + } + else if constexpr (std::is_same_v) + { + if (e.existed) + { + // This account is not always "touched". TODO: Why? + auto& a = get(e.addr); + a.nonce = 0; + a.code.clear(); + } + else + { + // TODO: Before Spurious Dragon we don't clear empty accounts ("erasable") + // so we need to delete them here explicitly. + // This should be changed by tuning "erasable" flag + // and clear in all revisions. + m_accounts.erase(e.addr); + } + } + else if constexpr (std::is_same_v) + { + auto& s = get(e.addr).storage.find(e.key)->second; + s.current = e.prev_value; + s.access_status = e.prev_access_status; + } + else if constexpr (std::is_same_v) + { + auto& s = get(e.addr).transient_storage.find(e.key)->second; + s = e.prev_value; + } + else if constexpr (std::is_same_v) + { + get(e.addr).balance = e.prev_balance; + } + else + { + // TODO(C++23): Change condition to `false` once CWG2518 is in. + static_assert(std::is_void_v, "unhandled journal entry type"); + } + }, + m_journal.back()); + m_journal.pop_back(); + } +} + +intx::uint256 compute_blob_gas_price(uint64_t excess_blob_gas) noexcept +{ + /// A helper function approximating `factor * e ** (numerator / denominator)`. + /// https://eips.ethereum.org/EIPS/eip-4844#helpers + static constexpr auto fake_exponential = [](uint64_t factor, uint64_t numerator, + uint64_t denominator) noexcept { + intx::uint256 i = 1; + intx::uint256 output = 0; + intx::uint256 numerator_accum = factor * denominator; + while (numerator_accum > 0) + { + output += numerator_accum; + numerator_accum = (numerator_accum * numerator) / (denominator * i); + i += 1; + } + return output / denominator; + }; + + static constexpr auto MIN_BLOB_GASPRICE = 1; + static constexpr auto BLOB_GASPRICE_UPDATE_FRACTION = 3338477; + return fake_exponential(MIN_BLOB_GASPRICE, excess_blob_gas, BLOB_GASPRICE_UPDATE_FRACTION); +} + /// Validates transaction and computes its execution gas limit (the amount of gas provided to EVM). -/// @return Non-negative execution gas limit for valid transaction -/// or negative value for invalid transaction. +/// @return Execution gas limit or transaction validation error. std::variant validate_transaction(const Account& sender_acc, - const BlockInfo& block, const Transaction& tx, evmc_revision rev) noexcept + const BlockInfo& block, const Transaction& tx, evmc_revision rev, int64_t block_gas_left, + int64_t blob_gas_left) noexcept { - if (rev < EVMC_LONDON && tx.kind == Transaction::Kind::eip1559) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + switch (tx.type) + { + case Transaction::Type::blob: + if (rev < EVMC_CANCUN) + return make_error_code(TX_TYPE_NOT_SUPPORTED); + if (!tx.to.has_value()) + return make_error_code(CREATE_BLOB_TX); + if (tx.blob_hashes.empty()) + return make_error_code(EMPTY_BLOB_HASHES_LIST); + + if (tx.max_blob_gas_price < block.blob_base_fee) + return make_error_code(FEE_CAP_LESS_THEN_BLOCKS); + + if (std::ranges::any_of(tx.blob_hashes, [](const auto& h) { return h.bytes[0] != 0x01; })) + return make_error_code(INVALID_BLOB_HASH_VERSION); + if (std::cmp_greater(tx.blob_gas_used(), blob_gas_left)) + return make_error_code(BLOB_GAS_LIMIT_EXCEEDED); + break; + + default:; + } + + switch (tx.type) + { + case Transaction::Type::blob: + case Transaction::Type::eip1559: + if (rev < EVMC_LONDON) + return make_error_code(TX_TYPE_NOT_SUPPORTED); - if (rev < EVMC_BERLIN && !tx.access_list.empty()) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + if (tx.max_priority_gas_price > tx.max_gas_price) + return make_error_code(TIP_GT_FEE_CAP); // Priority gas price is too high. + [[fallthrough]]; - if (tx.max_priority_gas_price > tx.max_gas_price) - return make_error_code(TIP_GT_FEE_CAP); // Priority gas price is too high. + case Transaction::Type::access_list: + if (rev < EVMC_BERLIN) + return make_error_code(TX_TYPE_NOT_SUPPORTED); + [[fallthrough]]; - if (tx.gas_limit > block.gas_limit) + case Transaction::Type::legacy:; + } + + assert(tx.max_priority_gas_price <= tx.max_gas_price); + + if (tx.gas_limit > block_gas_left) return make_error_code(GAS_LIMIT_REACHED); - if (rev >= EVMC_LONDON && tx.max_gas_price < block.base_fee) + if (tx.max_gas_price < block.base_fee) return make_error_code(FEE_CAP_LESS_THEN_BLOCKS); if (!sender_acc.code.empty()) return make_error_code(SENDER_NOT_EOA); // Origin must not be a contract (EIP-3607). - if (sender_acc.nonce == Account::NonceMax) + if (sender_acc.nonce == Account::NonceMax) // Nonce value limit (EIP-2681). return make_error_code(NONCE_HAS_MAX_VALUE); + if (sender_acc.nonce < tx.nonce) + return make_error_code(NONCE_TOO_HIGH); + + if (sender_acc.nonce > tx.nonce) + return make_error_code(NONCE_TOO_LOW); + // initcode size is limited by EIP-3860. - if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > max_initcode_size) + if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > MAX_INITCODE_SIZE) return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED); // Compute and check if sender has enough balance for the theoretical maximum transaction cost. // Note this is different from tx_max_cost computed with effective gas price later. // The computation cannot overflow if done with 512-bit precision. - if (const auto tx_cost_limit_512 = - umul(intx::uint256{tx.gas_limit}, tx.max_gas_price) + tx.value; - sender_acc.balance < tx_cost_limit_512) + auto max_total_fee = umul(intx::uint256{tx.gas_limit}, tx.max_gas_price); + max_total_fee += tx.value; + + if (tx.type == Transaction::Type::blob) + { + const auto total_blob_gas = tx.blob_gas_used(); + // FIXME: Can overflow uint256. + max_total_fee += total_blob_gas * tx.max_blob_gas_price; + } + if (sender_acc.balance < max_total_fee) return make_error_code(INSUFFICIENT_FUNDS); - const auto intrinsic_cost = compute_tx_intrinsic_cost(rev, tx); - if (intrinsic_cost > tx.gas_limit) + const auto execution_gas_limit = tx.gas_limit - compute_tx_intrinsic_cost(rev, tx); + if (execution_gas_limit < 0) return make_error_code(INTRINSIC_GAS_TOO_LOW); - return tx.gas_limit - intrinsic_cost; + return execution_gas_limit; } -evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept +namespace { - const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{}; - return { - tx.to.has_value() ? EVMC_CALL : EVMC_CREATE, - 0, - 0, - execution_gas_limit, - recipient, - tx.sender, - tx.data.data(), - tx.data.size(), - intx::be::store(tx.value), - {}, - recipient, - }; +/// Deletes "touched" (marked as erasable) empty accounts in the state. +void delete_empty_accounts(State& state) +{ + std::erase_if(state.get_accounts(), [](const std::pair& p) noexcept { + const auto& acc = p.second; + return acc.erase_if_empty && acc.is_empty(); + }); } } // namespace + void finalize(State& state, evmc_revision rev, const address& coinbase, - std::optional block_reward, std::span withdrawals) + std::optional block_reward, std::span ommers, + std::span withdrawals) { + // TODO: The block reward can be represented as a withdrawal. if (block_reward.has_value()) - state.touch(coinbase).balance += *block_reward; - - if (rev >= EVMC_SPURIOUS_DRAGON) { - std::erase_if( - state.get_accounts(), [](const std::pair& p) noexcept { - const auto& acc = p.second; - return acc.erasable && acc.is_empty(); - }); + const auto reward = *block_reward; + assert(reward % 32 == 0); // Assume block reward is divisible by 32. + const auto reward_by_32 = reward / 32; + const auto reward_by_8 = reward / 8; + + state.touch(coinbase).balance += reward + reward_by_32 * ommers.size(); + for (const auto& ommer : ommers) + { + assert(ommer.delta > 0 && ommer.delta < 8); + state.touch(ommer.beneficiary).balance += reward_by_8 * (8 - ommer.delta); + } } for (const auto& withdrawal : withdrawals) state.touch(withdrawal.recipient).balance += withdrawal.get_amount(); + + // Delete potentially empty block reward recipients. + if (rev >= EVMC_SPURIOUS_DRAGON) + delete_empty_accounts(state); } -std::variant transition( - State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm) +std::variant transition(State& state, const BlockInfo& block, + const Transaction& tx, evmc_revision rev, evmc::VM& vm, int64_t block_gas_left, + int64_t blob_gas_left) { - auto& sender_acc = state.get(tx.sender); - const auto validation_result = validate_transaction(sender_acc, block, tx, rev); + auto* sender_ptr = state.find(tx.sender); + + // Validate transaction. The validation needs the sender account, so in case + // it doesn't exist provide an empty one. The account isn't created in the state + // to prevent the state modification in case the transaction is invalid. + const auto validation_result = + validate_transaction((sender_ptr != nullptr) ? *sender_ptr : Account{}, block, tx, rev, + block_gas_left, blob_gas_left); if (holds_alternative(validation_result)) return get(validation_result); + // Once the transaction is valid, create new sender account. + // The account won't be empty because its nonce will be bumped. + auto& sender_acc = (sender_ptr != nullptr) ? *sender_ptr : state.insert(tx.sender); + + assert(sender_acc.nonce < Account::NonceMax); // Checked in transaction validation + ++sender_acc.nonce; // Bump sender nonce. + const auto execution_gas_limit = get(validation_result); const auto base_fee = (rev >= EVMC_LONDON) ? block.base_fee : 0; @@ -159,6 +419,13 @@ std::variant transition( sender_acc.balance -= tx_max_cost; // Modify sender balance after all checks. + if (tx.type == Transaction::Type::blob) + { + const auto blob_fee = tx.blob_gas_used() * block.blob_base_fee; + assert(sender_acc.balance >= blob_fee); // Checked at the front. + sender_acc.balance -= blob_fee; + } + Host host{rev, vm, state, block, tx}; sender_acc.access_status = EVMC_ACCESS_WARM; // Tx sender is always warm. @@ -177,7 +444,7 @@ std::variant transition( if (rev >= EVMC_SHANGHAI) host.access_account(block.coinbase); - const auto result = host.call(build_message(tx, execution_gas_limit)); + const auto result = host.call(build_message(tx, execution_gas_limit, rev)); auto gas_used = tx.gas_limit - result.gas_left; @@ -187,18 +454,40 @@ std::variant transition( gas_used -= refund; assert(gas_used > 0); - state.get(tx.sender).balance += tx_max_cost - gas_used * effective_gas_price; + sender_acc.balance += tx_max_cost - gas_used * effective_gas_price; state.touch(block.coinbase).balance += gas_used * priority_gas_price; // Apply destructs. std::erase_if(state.get_accounts(), [](const std::pair& p) noexcept { return p.second.destructed; }); - auto receipt = TransactionReceipt{tx.kind, result.status_code, gas_used, host.take_logs(), {}}; + // Cumulative gas used is unknown in this scope. + TransactionReceipt receipt{tx.type, result.status_code, gas_used, {}, host.take_logs(), {}, {}}; // Cannot put it into constructor call because logs are std::moved from host instance. receipt.logs_bloom_filter = compute_bloom_filter(receipt.logs); + // Delete empty accounts after every transaction. This is strictly required until Byzantium + // where intermediate state root hashes are part of the transaction receipt. + // TODO: Consider limiting this only to Spurious Dragon. + if (rev >= EVMC_SPURIOUS_DRAGON) + delete_empty_accounts(state); + + // Post-transaction clean-up. + // - Set accounts and their storage access status to cold. + // - Clear the "just created" account flag. + for (auto& [addr, acc] : state.get_accounts()) + { + acc.transient_storage.clear(); + acc.access_status = EVMC_ACCESS_COLD; + acc.just_created = false; + for (auto& [key, val] : acc.storage) + { + val.access_status = EVMC_ACCESS_COLD; + val.original = val.current; + } + } + return receipt; } @@ -209,28 +498,27 @@ std::variant transition( [[nodiscard]] bytes rlp_encode(const Transaction& tx) { - if (tx.kind == Transaction::Kind::legacy) + assert(tx.type <= Transaction::Type::blob); + + // TODO: Refactor this function. For all type of transactions most of the code is similar. + if (tx.type == Transaction::Type::legacy) { // rlp [nonce, gas_price, gas_limit, to, value, data, v, r, s]; return rlp::encode_tuple(tx.nonce, tx.max_gas_price, static_cast(tx.gas_limit), tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, tx.v, tx.r, tx.s); } - else if (tx.kind == Transaction::Kind::eip2930) + else if (tx.type == Transaction::Type::access_list) { - if (tx.v > 1) - throw std::invalid_argument("`v` value for eip2930 transaction must be 0 or 1"); // tx_type + - // rlp [nonce, gas_price, gas_limit, to, value, data, access_list, v, r, s]; + // rlp [chain_id, nonce, gas_price, gas_limit, to, value, data, access_list, v, r, s]; return bytes{0x01} + // Transaction type (eip2930 type == 1) rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_gas_price, static_cast(tx.gas_limit), tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, - tx.access_list, static_cast(tx.v), tx.r, tx.s); + tx.access_list, tx.v, tx.r, tx.s); } - else + else if (tx.type == Transaction::Type::eip1559) { - if (tx.v > 1) - throw std::invalid_argument("`v` value for eip1559 transaction must be 0 or 1"); // tx_type + // rlp [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, // data, access_list, sig_parity, r, s]; @@ -238,16 +526,91 @@ std::variant transition( rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_priority_gas_price, tx.max_gas_price, static_cast(tx.gas_limit), tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, - tx.access_list, static_cast(tx.v), tx.r, tx.s); + tx.access_list, tx.v, tx.r, tx.s); + } + else // Transaction::Type::blob + { + // tx_type + + // rlp [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, + // data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, sig_parity, r, s]; + return bytes{stdx::to_underlying(Transaction::Type::blob)} + + rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_priority_gas_price, tx.max_gas_price, + static_cast(tx.gas_limit), + tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, + tx.access_list, tx.max_blob_gas_price, tx.blob_hashes, tx.v, tx.r, tx.s); } } [[nodiscard]] bytes rlp_encode(const TransactionReceipt& receipt) { - const auto prefix = receipt.kind == Transaction::Kind::eip1559 ? bytes{0x02} : bytes{}; - return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS, - static_cast(receipt.gas_used), - bytes_view(receipt.logs_bloom_filter), receipt.logs); + if (receipt.post_state.has_value()) + { + assert(receipt.type == Transaction::Type::legacy); + + return rlp::encode_tuple(receipt.post_state.value(), + static_cast(receipt.cumulative_gas_used), + bytes_view(receipt.logs_bloom_filter), receipt.logs); + } + else + { + const auto prefix = receipt.type == Transaction::Type::legacy ? + bytes{} : + bytes{stdx::to_underlying(receipt.type)}; + + return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS, + static_cast(receipt.cumulative_gas_used), + bytes_view(receipt.logs_bloom_filter), receipt.logs); + } +} + +[[nodiscard]] bytes rlp_encode(const Withdrawal& withdrawal) +{ + return rlp::encode_tuple(withdrawal.index, withdrawal.validator_index, withdrawal.recipient, + withdrawal.amount_in_gwei); +} + +[[nodiscard]] std::string get_tests_invalid_tx_message(ErrorCode errc) noexcept +{ + switch (errc) + { + case SUCCESS: + return ""; + case INTRINSIC_GAS_TOO_LOW: + return "TR_IntrinsicGas"; + case TX_TYPE_NOT_SUPPORTED: + return "TR_TypeNotSupported"; + case INSUFFICIENT_FUNDS: + return "TR_NoFunds"; + case NONCE_HAS_MAX_VALUE: + return "TR_NonceHasMaxValue:"; + case NONCE_TOO_HIGH: + return "TR_NonceTooHigh"; + case NONCE_TOO_LOW: + return "TR_NonceTooLow"; + case TIP_GT_FEE_CAP: + return "TR_TipGtFeeCap"; + case FEE_CAP_LESS_THEN_BLOCKS: + return "TR_FeeCapLessThanBlocks"; + case GAS_LIMIT_REACHED: + return "TR_GasLimitReached"; + case SENDER_NOT_EOA: + return "SenderNotEOA"; + case INIT_CODE_SIZE_LIMIT_EXCEEDED: + return "TR_InitCodeLimitExceeded"; + case CREATE_BLOB_TX: + return "TR_BLOBCREATE"; + case EMPTY_BLOB_HASHES_LIST: + return "TR_EMPTYBLOB"; + case INVALID_BLOB_HASH_VERSION: + return "TR_BLOBVERSION_INVALID"; + case BLOB_GAS_LIMIT_EXCEEDED: + return "TR_BLOBLIST_OVERSIZE"; + case UNKNOWN_ERROR: + return "Unknown error"; + default: + assert(false); + return "Wrong error code"; + } } } // namespace evmone::state diff --git a/test/state/state.hpp b/test/state/state.hpp index 91d9b373..3683ac2a 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -6,6 +6,7 @@ #include "account.hpp" #include "bloom_filter.hpp" +#include "errors.hpp" #include "hash_utils.hpp" #include #include @@ -14,60 +15,123 @@ namespace evmone::state { +/// The Ethereum State: the collection of accounts mapped by their addresses. class State { + struct JournalBase + { + address addr; + }; + + struct JournalBalanceChange : JournalBase + { + intx::uint256 prev_balance; + }; + + struct JournalTouched : JournalBase + {}; + + struct JournalStorageChange : JournalBase + { + bytes32 key; + bytes32 prev_value; + evmc_access_status prev_access_status; + }; + + struct JournalTransientStorageChange : JournalBase + { + bytes32 key; + bytes32 prev_value; + }; + + struct JournalNonceBump : JournalBase + {}; + + struct JournalCreate : JournalBase + { + bool existed; + }; + + struct JournalDestruct : JournalBase + {}; + + struct JournalAccessAccount : JournalBase + {}; + + using JournalEntry = + std::variant; + std::unordered_map m_accounts; + /// The state journal: the list of changes made in the state + /// with information how to revert them. + std::vector m_journal; + public: + State() = default; + State(const State&) = delete; + State(State&&) = default; + State& operator=(State&&) = default; + /// Inserts the new account at the address. /// There must not exist any account under this address before. - Account& insert(const address& addr, Account account = {}) - { - const auto r = m_accounts.insert({addr, std::move(account)}); - assert(r.second); - return r.first->second; - } + Account& insert(const address& addr, Account account = {}); /// Returns the pointer to the account at the address if the account exists. Null otherwise. - Account* find(const address& addr) noexcept - { - const auto it = m_accounts.find(addr); - if (it != m_accounts.end()) - return &it->second; - return nullptr; - } + Account* find(const address& addr) noexcept; /// Gets the account at the address (the account must exist). - Account& get(const address& addr) noexcept - { - auto acc = find(addr); - assert(acc != nullptr); - return *acc; - } + Account& get(const address& addr) noexcept; /// Gets an existing account or inserts new account. - Account& get_or_insert(const address& addr, Account account = {}) - { - if (const auto acc = find(addr); acc != nullptr) - return *acc; - return insert(addr, std::move(account)); - } - - /// Touches (as in EIP-161) an existing account or inserts new erasable account. - Account& touch(const address& addr) - { - auto& acc = get_or_insert(addr); - acc.erasable = true; - return acc; - } + Account& get_or_insert(const address& addr, Account account = {}); [[nodiscard]] auto& get_accounts() noexcept { return m_accounts; } [[nodiscard]] const auto& get_accounts() const noexcept { return m_accounts; } + + /// Returns the state journal checkpoint. It can be later used to in rollback() + /// to revert changes newer than the checkpoint. + [[nodiscard]] size_t checkpoint() const noexcept { return m_journal.size(); } + + /// Reverts state changes made after the checkpoint. + void rollback(size_t checkpoint); + + /// Methods performing changes to the state which can be reverted by rollback(). + /// @{ + + /// Touches (as in EIP-161) an existing account or inserts new erasable account. + Account& touch(const address& addr); + + void journal_balance_change(const address& addr, const intx::uint256& prev_balance); + + void journal_storage_change(const address& addr, const bytes32& key, const StorageValue& value); + + void journal_transient_storage_change( + const address& addr, const bytes32& key, const bytes32& value); + + void journal_bump_nonce(const address& addr); + + void journal_create(const address& addr, bool existed); + + void journal_destruct(const address& addr); + + void journal_access_account(const address& addr); + + /// @} +}; + +struct Ommer +{ + address beneficiary; ///< Ommer block beneficiary address. + uint32_t delta = 0; ///< Difference between current and ommer block number. }; struct Withdrawal { + uint64_t index = 0; + uint64_t validator_index = 0; address recipient; uint64_t amount_in_gwei = 0; ///< The amount is denominated in gwei. @@ -80,35 +144,78 @@ struct Withdrawal struct BlockInfo { + /// Max amount of blob gas allowed in block. It's constant now but can be dynamic in the future. + static constexpr int64_t MAX_BLOB_GAS_PER_BLOCK = 786432; + int64_t number = 0; int64_t timestamp = 0; + int64_t parent_timestamp = 0; int64_t gas_limit = 0; address coinbase; + int64_t difficulty = 0; + int64_t parent_difficulty = 0; + hash256 parent_ommers_hash; bytes32 prev_randao; + hash256 parent_beacon_block_root; uint64_t base_fee = 0; + + /// The "excess blob gas" parameter from EIP-4844 + /// for computing the blob gas price in the current block. + uint64_t excess_blob_gas = 0; + + /// The blob gas price parameter from EIP-4844. + /// This values is not stored in block headers directly but computed from excess_blob_gas. + intx::uint256 blob_base_fee = 0; + + std::vector ommers; std::vector withdrawals; + std::unordered_map known_block_hashes; }; using AccessList = std::vector>>; struct Transaction { - enum class Kind : uint8_t + /// The type of the transaction. + /// + /// The format is defined by EIP-2718: Typed Transaction Envelope. + /// https://eips.ethereum.org/EIPS/eip-2718. + enum class Type : uint8_t { + /// The legacy RLP-encoded transaction without leading "type" byte. legacy = 0, - eip2930 = 1, ///< Transaction with access list https://eips.ethereum.org/EIPS/eip-2930 - eip1559 = 2 ///< EIP1559 transaction https://eips.ethereum.org/EIPS/eip-1559 + + /// The typed transaction with optional account/storage access list. + /// Introduced by EIP-2930 https://eips.ethereum.org/EIPS/eip-2930. + access_list = 1, + + /// The typed transaction with priority gas price. + /// Introduced by EIP-1559 https://eips.ethereum.org/EIPS/eip-1559. + eip1559 = 2, + + /// The typed blob transaction (with array of blob hashes). + /// Introduced by EIP-4844 https://eips.ethereum.org/EIPS/eip-4844. + blob = 3, }; - Kind kind = Kind::legacy; + /// Returns amount of blob gas used by this transaction + [[nodiscard]] int64_t blob_gas_used() const + { + static constexpr auto GAS_PER_BLOB = 0x20000; + return GAS_PER_BLOB * static_cast(blob_hashes.size()); + } + + Type type = Type::legacy; bytes data; int64_t gas_limit; intx::uint256 max_gas_price; intx::uint256 max_priority_gas_price; + intx::uint256 max_blob_gas_price; address sender; std::optional
to; intx::uint256 value; AccessList access_list; + std::vector blob_hashes; uint64_t chain_id = 0; uint64_t nonce = 0; intx::uint256 r; @@ -123,24 +230,50 @@ struct Log std::vector topics; }; +/// Transaction Receipt +/// +/// This struct is used in two contexts: +/// 1. As the formally specified, RLP-encode transaction receipt included in the Ethereum blocks. +/// 2. As the internal representation of the transaction execution result. +/// These both roles share most, but not all the information. There are some fields that cannot be +/// assigned in the single transaction execution context. There are also fields that are not a part +/// of the RLP-encoded transaction receipts. +/// TODO: Consider splitting the struct into two based on the duality explained above. struct TransactionReceipt { - Transaction::Kind kind = Transaction::Kind::legacy; + Transaction::Type type = Transaction::Type::legacy; evmc_status_code status = EVMC_INTERNAL_ERROR; + + /// Amount of gas used by this transaction. int64_t gas_used = 0; + + /// Amount of gas used by this and previous transactions in the block. + int64_t cumulative_gas_used = 0; std::vector logs; BloomFilter logs_bloom_filter; + + /// Root hash of the state after this transaction. Used only in old pre-Byzantium transactions. + std::optional post_state; }; +/// Computes the current blob gas price based on the excess blob gas. +intx::uint256 compute_blob_gas_price(uint64_t excess_blob_gas) noexcept; + /// Finalize state after applying a "block" of transactions. /// /// Applies block reward to coinbase, withdrawals (post Shanghai) and deletes empty touched accounts /// (post Spurious Dragon). void finalize(State& state, evmc_revision rev, const address& coinbase, - std::optional block_reward, std::span withdrawals); + std::optional block_reward, std::span ommers, + std::span withdrawals); + +[[nodiscard]] std::variant transition(State& state, + const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm, + int64_t block_gas_left, int64_t blob_gas_left); -[[nodiscard]] std::variant transition( - State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm); +std::variant validate_transaction(const Account& sender_acc, + const BlockInfo& block, const Transaction& tx, evmc_revision rev, int64_t block_gas_left, + int64_t blob_gas_left) noexcept; /// Defines how to RLP-encode a Transaction. [[nodiscard]] bytes rlp_encode(const Transaction& tx); @@ -151,4 +284,9 @@ void finalize(State& state, evmc_revision rev, const address& coinbase, /// Defines how to RLP-encode a Log. [[nodiscard]] bytes rlp_encode(const Log& log); +/// Defines how to RLP-encode a Withdrawal. +[[nodiscard]] bytes rlp_encode(const Withdrawal& withdrawal); + +[[nodiscard]] std::string get_tests_invalid_tx_message(ErrorCode errc) noexcept; + } // namespace evmone::state diff --git a/test/state/system_contracts.cpp b/test/state/system_contracts.cpp new file mode 100644 index 00000000..55219db6 --- /dev/null +++ b/test/state/system_contracts.cpp @@ -0,0 +1,78 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "system_contracts.hpp" +#include "host.hpp" +#include "state.hpp" + +namespace evmone::state +{ +namespace +{ +/// Information about a registered system contract. +struct SystemContract +{ + using GetInputFn = bytes_view(const BlockInfo&) noexcept; + + evmc_revision since = EVMC_MAX_REVISION; ///< EVM revision in which added. + address addr; ///< Address of the system contract. + GetInputFn* get_input = nullptr; ///< How to get the input for the system call. +}; + +/// Registered system contracts. +constexpr std::array SYSTEM_CONTRACTS{ + SystemContract{EVMC_CANCUN, BEACON_ROOTS_ADDRESS, + [](const BlockInfo& block) noexcept { return bytes_view{block.parent_beacon_block_root}; }}, + SystemContract{EVMC_PRAGUE, HISTORY_STORAGE_ADDRESS, + [](const BlockInfo& block) noexcept { + return bytes_view{block.known_block_hashes.at(block.number - 1)}; + }}, +}; + +static_assert(std::ranges::is_sorted(SYSTEM_CONTRACTS, + [](const auto& a, const auto& b) noexcept { return a.since < b.since; }), + "system contract entries must be ordered by revision"); + +} // namespace + +void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm) +{ + for (const auto& [since, addr, get_input] : SYSTEM_CONTRACTS) + { + if (rev < since) + return; // Because entries are ordered, there are no other contracts for this revision. + + // Skip the call if the target account doesn't exist. This is by EIP-4788 spec. + // > if no code exists at [address], the call must fail silently. + const auto acc = state.find(addr); + if (acc == nullptr) + continue; + + const auto input = get_input(block); + + const evmc_message msg{ + .kind = EVMC_CALL, + .gas = 30'000'000, + .recipient = addr, + .sender = SYSTEM_ADDRESS, + .input_data = input.data(), + .input_size = input.size(), + }; + + const Transaction empty_tx{}; + Host host{rev, vm, state, block, empty_tx}; + const auto& code = acc->code; + [[maybe_unused]] const auto res = vm.execute(host, rev, msg, code.data(), code.size()); + assert(res.status_code == EVMC_SUCCESS); + assert(acc->access_status == EVMC_ACCESS_COLD); + + // Reset storage status. + for (auto& [_, val] : acc->storage) + { + val.access_status = EVMC_ACCESS_COLD; + val.original = val.current; + } + } +} +} // namespace evmone::state diff --git a/test/state/system_contracts.hpp b/test/state/system_contracts.hpp new file mode 100644 index 00000000..b3e6114a --- /dev/null +++ b/test/state/system_contracts.hpp @@ -0,0 +1,29 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::state +{ +using namespace evmc::literals; + +/// The address of the sender of the system calls (EIP-4788). +constexpr auto SYSTEM_ADDRESS = 0xfffffffffffffffffffffffffffffffffffffffe_address; + +/// The address of the system contract storing the root hashes of beacon chain blocks (EIP-4788). +constexpr auto BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address; + +/// The address of the system contract storing historical block hashes (EIP-2935). +constexpr auto HISTORY_STORAGE_ADDRESS = 0x0aae40965e6800cd9b1f4b05ff21581047e3f91e_address; + +struct BlockInfo; +class State; + +/// Performs the system call: invokes system contracts. +/// +/// Executes code of pre-defined accounts via pseudo-transaction from the system sender (0xff...fe). +/// The sender's nonce is not increased. +void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm); +} // namespace evmone::state diff --git a/test/state/test_state.cpp b/test/state/test_state.cpp new file mode 100644 index 00000000..c0073d05 --- /dev/null +++ b/test/state/test_state.cpp @@ -0,0 +1,34 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#include "test_state.hpp" +#include "state.hpp" + +namespace evmone::test +{ +TestState::TestState(const state::State& intra_state) +{ + for (const auto& [addr, acc] : intra_state.get_accounts()) + { + auto& test_acc = + (*this)[addr] = {.nonce = acc.nonce, .balance = acc.balance, .code = acc.code}; + auto& test_storage = test_acc.storage; + for (const auto& [key, value] : acc.storage) + test_storage[key] = value.current; + } +} + +state::State TestState::to_intra_state() const +{ + state::State intra_state; + for (const auto& [addr, acc] : *this) + { + auto& intra_acc = intra_state.insert( + addr, {.nonce = acc.nonce, .balance = acc.balance, .code = acc.code}); + auto& storage = intra_acc.storage; + for (const auto& [key, value] : acc.storage) + storage[key] = {.current = value, .original = value}; + } + return intra_state; +} +} // namespace evmone::test diff --git a/test/state/test_state.hpp b/test/state/test_state.hpp new file mode 100644 index 00000000..f9377124 --- /dev/null +++ b/test/state/test_state.hpp @@ -0,0 +1,67 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +namespace evmone +{ +namespace state +{ +class State; +} + +namespace test +{ +using evmc::address; +using evmc::bytes; +using evmc::bytes32; +using intx::uint256; + +/// Ethereum account representation for tests. +struct TestAccount +{ + uint64_t nonce = 0; + uint256 balance; + std::map storage; + bytes code; + + bool operator==(const TestAccount&) const noexcept = default; +}; + +/// Ethereum State representation for tests. +/// +/// This is a simplified variant of state::State: +/// it hides some details related to transaction execution (e.g. original storage values) +/// and is also easier to work with in tests. +class TestState : public std::map +{ +public: + using map::map; + + /// Inserts new account to the state. + /// + /// This method is for compatibility with state::State::insert(). + /// Don't use it in new tests, use std::map interface instead. + /// TODO: deprecate this method. + void insert(const address& addr, TestAccount&& acc) { (*this)[addr] = std::move(acc); } + + /// Gets the reference to an existing account. + /// + /// This method is for compatibility with state::State::get(). + /// Don't use it in new tests, use std::map interface instead. + /// TODO: deprecate this method. + TestAccount& get(const address& addr) { return (*this)[addr]; } + + /// Converts the intra state to TestState. + explicit TestState(const state::State& intra_state); + + /// Converts the TestState to intra state for transaction execution. + [[nodiscard]] state::State to_intra_state() const; +}; + +} // namespace test +} // namespace evmone diff --git a/test/statetest/CMakeLists.txt b/test/statetest/CMakeLists.txt index 7b2ea11f..eaf85dfe 100644 --- a/test/statetest/CMakeLists.txt +++ b/test/statetest/CMakeLists.txt @@ -2,23 +2,22 @@ # Copyright 2022 The evmone Authors. # SPDX-License-Identifier: Apache-2.0 -hunter_add_package(nlohmann_json) -find_package(nlohmann_json CONFIG REQUIRED) - add_library(evmone-statetestutils STATIC) add_library(evmone::statetestutils ALIAS evmone-statetestutils) target_compile_features(evmone-statetestutils PUBLIC cxx_std_20) -target_link_libraries(evmone-statetestutils PRIVATE evmone::state nlohmann_json::nlohmann_json) +target_link_libraries(evmone-statetestutils PRIVATE evmone::state evmone::testutils nlohmann_json::nlohmann_json) target_include_directories(evmone-statetestutils PRIVATE ${evmone_private_include_dir}) target_sources( evmone-statetestutils PRIVATE + ../blockchaintest/blockchaintest_loader.cpp statetest.hpp + statetest_export.cpp statetest_loader.cpp statetest_logs_hash.cpp ) add_executable(evmone-statetest) -target_link_libraries(evmone-statetest PRIVATE evmone::statetestutils evmone GTest::gtest) +target_link_libraries(evmone-statetest PRIVATE evmone::statetestutils evmone evmone-buildinfo GTest::gtest) target_include_directories(evmone-statetest PRIVATE ${evmone_private_include_dir}) target_sources( evmone-statetest PRIVATE diff --git a/test/statetest/statetest.cpp b/test/statetest/statetest.cpp index e9894251..8a78a259 100644 --- a/test/statetest/statetest.cpp +++ b/test/statetest/statetest.cpp @@ -5,36 +5,42 @@ #include "statetest.hpp" #include #include +#include #include #include +namespace fs = std::filesystem; + namespace { class StateTest : public testing::Test { fs::path m_json_test_file; evmc::VM& m_vm; + bool m_trace = false; public: - explicit StateTest(fs::path json_test_file, evmc::VM& vm) noexcept - : m_json_test_file{std::move(json_test_file)}, m_vm{vm} + explicit StateTest(fs::path json_test_file, evmc::VM& vm, bool trace) noexcept + : m_json_test_file{std::move(json_test_file)}, m_vm{vm}, m_trace{trace} {} void TestBody() final { std::ifstream f{m_json_test_file}; - evmone::test::run_state_test(evmone::test::load_state_test(f), m_vm); + const auto tests = evmone::test::load_state_tests(f); + for (const auto& test : tests) + evmone::test::run_state_test(test, m_vm, m_trace); } }; -void register_test(const std::string& suite_name, const fs::path& file, evmc::VM& vm) +void register_test(const std::string& suite_name, const fs::path& file, evmc::VM& vm, bool trace) { testing::RegisterTest(suite_name.c_str(), file.stem().string().c_str(), nullptr, nullptr, file.string().c_str(), 0, - [file, &vm]() -> testing::Test* { return new StateTest(file, vm); }); + [file, &vm, trace]() -> testing::Test* { return new StateTest(file, vm, trace); }); } -void register_test_files(const fs::path& root, evmc::VM& vm) +void register_test_files(const fs::path& root, evmc::VM& vm, bool trace) { if (is_directory(root)) { @@ -46,11 +52,11 @@ void register_test_files(const fs::path& root, evmc::VM& vm) std::sort(test_files.begin(), test_files.end()); for (const auto& p : test_files) - register_test(fs::relative(p, root).parent_path().string(), p, vm); + register_test(fs::relative(p, root).parent_path().string(), p, vm, trace); } else // Treat as a file. { - register_test(root.parent_path().string(), root, vm); + register_test(root.parent_path().string(), root, vm, trace); } } } // namespace @@ -75,23 +81,31 @@ int main(int argc, char* argv[]) CLI::App app{"evmone state test runner"}; + app.set_version_flag("--version", "evmone-statetest " EVMONE_VERSION); + std::vector paths; app.add_option("path", paths, "Path to test file or directory") ->required() ->check(CLI::ExistingPath); - bool trace_flag = false; - app.add_flag("--trace", trace_flag, "Enable EVM tracing"); + bool trace = false; + bool trace_summary = false; + const auto trace_opt = app.add_flag("--trace", trace, "Enable EVM tracing"); + app.add_flag("--trace-summary", trace_summary, "Output trace summary only") + ->excludes(trace_opt); CLI11_PARSE(app, argc, argv); evmc::VM vm{evmc_create_evmone(), {{"O", "0"}}}; - if (trace_flag) + if (trace) + { + std::ios::sync_with_stdio(false); vm.set_option("trace", "1"); + } for (const auto& p : paths) - register_test_files(p, vm); + register_test_files(p, vm, trace || trace_summary); return RUN_ALL_TESTS(); } diff --git a/test/statetest/statetest.hpp b/test/statetest/statetest.hpp index a4cf8f54..e4ecff11 100644 --- a/test/statetest/statetest.hpp +++ b/test/statetest/statetest.hpp @@ -4,18 +4,14 @@ #pragma once #include "../state/state.hpp" +#include "../state/test_state.hpp" #include -#include -namespace fs = std::filesystem; namespace json = nlohmann; namespace evmone::test { -/// Translates tests fork name to EVM revision -evmc_revision to_rev(std::string_view s); - struct TestMultiTransaction : state::Transaction { struct Indexes @@ -50,7 +46,7 @@ struct StateTransitionTest { TestMultiTransaction::Indexes indexes; hash256 state_hash; - hash256 logs_hash; + hash256 logs_hash = EmptyListHash; bool exception = false; }; @@ -58,7 +54,8 @@ struct StateTransitionTest std::vector expectations; }; - state::State pre_state; + std::string name; + TestState pre_state; state::BlockInfo block; TestMultiTransaction multi_tx; std::vector cases; @@ -74,22 +71,42 @@ uint64_t from_json(const json::json& j); template <> int64_t from_json(const json::json& j); +template <> +address from_json
(const json::json& j); + +template <> +hash256 from_json(const json::json& j); + +template <> +bytes from_json(const json::json& j); + template <> state::BlockInfo from_json(const json::json& j); template <> -state::State from_json(const json::json& j); +state::Withdrawal from_json(const json::json& j); + +template <> +TestState from_json(const json::json& j); template <> state::Transaction from_json(const json::json& j); -StateTransitionTest load_state_test(std::istream& input); +/// Exports the State (accounts) to JSON format (aka pre/post/alloc state). +json::json to_json(const TestState& state); -/// Validates deployed EOF containers before running state test. -/// Throws exception on any invalid EOF in state. -void validate_deployed_code(const state::State& state, evmc_revision rev); +std::vector load_state_tests(std::istream& input); -void run_state_test(const StateTransitionTest& test, evmc::VM& vm); +/// Validates an Ethereum state: +/// - checks that there are no zero-value storage entries, +/// - checks that there are no invalid EOF codes. +/// Throws std::invalid_argument exception. +void validate_state(const TestState& state, evmc_revision rev); + +/// Execute the state @p test using the @p vm. +/// +/// @param trace_summary Output execution summary to the default trace stream. +void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_summary); /// Computes the hash of the RLP-encoded list of transaction logs. /// This method is only used in tests. @@ -108,5 +125,4 @@ inline std::string hex0x(const bytes_view& v) { return "0x" + evmc::hex(v); } - } // namespace evmone::test diff --git a/test/statetest/statetest_export.cpp b/test/statetest/statetest_export.cpp new file mode 100644 index 00000000..d60b621f --- /dev/null +++ b/test/statetest/statetest_export.cpp @@ -0,0 +1,28 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "statetest.hpp" + +namespace evmone::test +{ +json::json to_json(const TestState& state) +{ + json::json j = json::json::object(); + for (const auto& [addr, acc] : state) + { + auto& j_acc = j[hex0x(addr)]; + j_acc["nonce"] = hex0x(acc.nonce); + j_acc["balance"] = hex0x(acc.balance); + j_acc["code"] = hex0x(bytes_view(acc.code.data(), acc.code.size())); + + auto& j_storage = j_acc["storage"] = json::json::object(); + for (const auto& [key, val] : acc.storage) + { + if (!is_zero(val)) + j_storage[hex0x(key)] = hex0x(val); + } + } + return j; +} +} // namespace evmone::test diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 0bf67417..4d60be09 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "../utils/stdx/utility.hpp" +#include "../utils/utils.hpp" #include "statetest.hpp" #include #include @@ -12,6 +13,17 @@ namespace evmone::test namespace json = nlohmann; using evmc::from_hex; +namespace +{ +template +T load_if_exists(const json::json& j, std::string_view key) +{ + if (const auto it = j.find(key); it != j.end()) + return from_json(*it); + return {}; +} +} // namespace + template <> uint8_t from_json(const json::json& j) { @@ -32,13 +44,12 @@ static std::optional integer_from_json(const json::json& j) return {}; const auto s = j.get(); - size_t num_processed = 0; - T v = 0; - if constexpr (std::is_same_v) - v = std::stoull(s, &num_processed, 0); - else - v = std::stoll(s, &num_processed, 0); + // Always load integers as unsigned and cast to the required type. + // This will work for cases where a test case uses uint64 timestamps while we use int64. + // TODO: Change timestamp type to uint64. + size_t num_processed = 0; + const auto v = static_cast(std::stoull(s, &num_processed, 0)); if (num_processed == 0 || num_processed != s.size()) return {}; return v; @@ -77,12 +88,14 @@ address from_json
(const json::json& j) template <> hash256 from_json(const json::json& j) { - // Special case to handle "0". Required by exec-spec-tests. - // TODO: Get rid of it. - if (j.is_string() && (j == "0" || j == "0x0")) - return 0x00_bytes32; - else - return evmc::from_hex(j.get()).value(); + const auto s = j.get(); + if (s == "0" || s == "0x0") // Special case to handle "0". Required by exec-spec-tests. + return 0x00_bytes32; // TODO: Get rid of it. + + const auto opt_hash = evmc::from_hex(s); + if (!opt_hash) + throw std::invalid_argument("invalid hash: " + s); + return *opt_hash; } template <> @@ -147,19 +160,40 @@ inline uint64_t calculate_current_base_fee_eip1559( return base_fee; } +template <> +state::Withdrawal from_json(const json::json& j) +{ + return {from_json(j.at("index")), from_json(j.at("validatorIndex")), + from_json(j.at("address")), from_json(j.at("amount"))}; +} + template <> state::BlockInfo from_json(const json::json& j) { - evmc::bytes32 difficulty; + evmc::bytes32 prev_randao; + int64_t current_difficulty = 0; + int64_t parent_difficulty = 0; const auto prev_randao_it = j.find("currentRandom"); const auto current_difficulty_it = j.find("currentDifficulty"); const auto parent_difficulty_it = j.find("parentDifficulty"); + + if (current_difficulty_it != j.end()) + current_difficulty = from_json(*current_difficulty_it); + if (parent_difficulty_it != j.end()) + parent_difficulty = from_json(*parent_difficulty_it); + + // When it's not defined init it with difficulty value. if (prev_randao_it != j.end()) - difficulty = from_json(*prev_randao_it); + prev_randao = from_json(*prev_randao_it); else if (current_difficulty_it != j.end()) - difficulty = from_json(*current_difficulty_it); + prev_randao = from_json(*current_difficulty_it); else if (parent_difficulty_it != j.end()) - difficulty = from_json(*parent_difficulty_it); + prev_randao = from_json(*parent_difficulty_it); + + hash256 parent_uncle_hash; + const auto parent_uncle_hash_it = j.find("parentUncleHash"); + if (parent_uncle_hash_it != j.end()) + parent_uncle_hash = from_json(*parent_uncle_hash_it); uint64_t base_fee = 0; if (j.contains("currentBaseFee")) @@ -175,88 +209,110 @@ state::BlockInfo from_json(const json::json& j) if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end()) { for (const auto& withdrawal : *withdrawals_it) + withdrawals.push_back(from_json(withdrawal)); + } + + std::unordered_map block_hashes; + if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end()) + { + for (const auto& [j_num, j_hash] : block_hashes_it->items()) + block_hashes[from_json(j_num)] = from_json(j_hash); + } + + std::vector ommers; + if (const auto ommers_it = j.find("ommers"); ommers_it != j.end()) + { + for (const auto& ommer : *ommers_it) { - withdrawals.push_back({from_json(withdrawal.at("address")), - from_json(withdrawal.at("amount"))}); + ommers.push_back( + {from_json(ommer.at("address")), ommer.at("delta").get()}); } } - return {from_json(j.at("currentNumber")), from_json(j.at("currentTimestamp")), - from_json(j.at("currentGasLimit")), - from_json(j.at("currentCoinbase")), difficulty, base_fee, - std::move(withdrawals)}; + int64_t parent_timestamp = 0; + auto parent_timestamp_it = j.find("parentTimestamp"); + if (parent_timestamp_it != j.end()) + parent_timestamp = from_json(*parent_timestamp_it); + + uint64_t excess_blob_gas = 0; + if (const auto it = j.find("parentExcessBlobGas"); it != j.end()) + { + const auto parent_excess_blob_gas = from_json(*it); + const auto parent_blob_gas_used = from_json(j.at("parentBlobGasUsed")); + static constexpr uint64_t TARGET_BLOB_GAS_PER_BLOCK = 0x60000; + excess_blob_gas = + std::max(parent_excess_blob_gas + parent_blob_gas_used, TARGET_BLOB_GAS_PER_BLOCK) - + TARGET_BLOB_GAS_PER_BLOCK; + } + else if (const auto it2 = j.find("currentExcessBlobGas"); it2 != j.end()) + { + excess_blob_gas = from_json(*it2); + } + + return state::BlockInfo{ + .number = from_json(j.at("currentNumber")), + .timestamp = from_json(j.at("currentTimestamp")), + .parent_timestamp = parent_timestamp, + .gas_limit = from_json(j.at("currentGasLimit")), + .coinbase = from_json(j.at("currentCoinbase")), + .difficulty = current_difficulty, + .parent_difficulty = parent_difficulty, + .parent_ommers_hash = parent_uncle_hash, + .prev_randao = prev_randao, + .parent_beacon_block_root = load_if_exists(j, "parentBeaconBlockRoot"), + .base_fee = base_fee, + .excess_blob_gas = excess_blob_gas, + .blob_base_fee = state::compute_blob_gas_price(excess_blob_gas), + .ommers = std::move(ommers), + .withdrawals = std::move(withdrawals), + .known_block_hashes = std::move(block_hashes), + }; } template <> -state::State from_json(const json::json& j) +TestState from_json(const json::json& j) { - state::State o; + TestState o; + assert(j.is_object()); for (const auto& [j_addr, j_acc] : j.items()) { - auto& acc = o.insert(from_json
(j_addr), - {.nonce = from_json(j_acc.at("nonce")), + auto& acc = + o[from_json
(j_addr)] = {.nonce = from_json(j_acc.at("nonce")), .balance = from_json(j_acc.at("balance")), - .code = from_json(j_acc.at("code"))}); + .code = from_json(j_acc.at("code"))}; if (const auto storage_it = j_acc.find("storage"); storage_it != j_acc.end()) { for (const auto& [j_key, j_value] : storage_it->items()) { - const auto value = from_json(j_value); - acc.storage.insert( - {from_json(j_key), {.current = value, .original = value}}); + if (const auto value = from_json(j_value); !is_zero(value)) + acc.storage[from_json(j_key)] = value; } } } return o; } -evmc_revision to_rev(std::string_view s) -{ - if (s == "Frontier") - return EVMC_FRONTIER; - if (s == "Homestead") - return EVMC_HOMESTEAD; - if (s == "EIP150") - return EVMC_TANGERINE_WHISTLE; - if (s == "EIP158") - return EVMC_SPURIOUS_DRAGON; - if (s == "Byzantium") - return EVMC_BYZANTIUM; - if (s == "Constantinople") - return EVMC_CONSTANTINOPLE; - if (s == "ConstantinopleFix") - return EVMC_PETERSBURG; - if (s == "Istanbul") - return EVMC_ISTANBUL; - if (s == "Berlin") - return EVMC_BERLIN; - if (s == "London") - return EVMC_LONDON; - if (s == "Merge") - return EVMC_PARIS; - if (s == "Merge+3855") // PUSH0 - return EVMC_SHANGHAI; - if (s == "Shanghai") - return EVMC_SHANGHAI; - if (s == "Cancun") - return EVMC_CANCUN; - if (s == "Prague") - return EVMC_PRAGUE; - throw std::invalid_argument{"unknown revision: " + std::string{s}}; -} - /// Load common parts of Transaction or TestMultiTransaction. static void from_json_tx_common(const json::json& j, state::Transaction& o) { o.sender = from_json(j.at("sender")); + o.nonce = from_json(j.at("nonce")); + + if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end()) + o.chain_id = from_json(*chain_id_it); + else + o.chain_id = 1; - if (const auto to_it = j.find("to"); to_it != j.end() && !to_it->get().empty()) - o.to = from_json(*to_it); + if (const auto to_it = j.find("to"); to_it != j.end()) + { + if (!to_it->is_null() && !to_it->get().empty()) + o.to = from_json(*to_it); + } if (const auto gas_price_it = j.find("gasPrice"); gas_price_it != j.end()) { - o.kind = state::Transaction::Kind::legacy; + o.type = state::Transaction::Type::legacy; o.max_gas_price = from_json(*gas_price_it); o.max_priority_gas_price = o.max_gas_price; if (j.contains("maxFeePerGas") || j.contains("maxPriorityFeePerGas")) @@ -267,10 +323,20 @@ static void from_json_tx_common(const json::json& j, state::Transaction& o) } else { - o.kind = state::Transaction::Kind::eip1559; + o.type = state::Transaction::Type::eip1559; o.max_gas_price = from_json(j.at("maxFeePerGas")); o.max_priority_gas_price = from_json(j.at("maxPriorityFeePerGas")); } + + if (const auto it = j.find("maxFeePerBlobGas"); it != j.end()) + o.max_blob_gas_price = from_json(*it); + + if (const auto it = j.find("blobVersionedHashes"); it != j.end()) + { + o.type = state::Transaction::Type::blob; + for (const auto& hash : *it) + o.blob_hashes.push_back(from_json(hash)); + } } template <> @@ -278,23 +344,33 @@ state::Transaction from_json(const json::json& j) { state::Transaction o; from_json_tx_common(j, o); - if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end()) - o.chain_id = from_json(*chain_id_it); - o.data = from_json(j.at("input")); - o.gas_limit = from_json(j.at("gas")); + + if (const auto it = j.find("data"); it != j.end()) + o.data = from_json(*it); + else + o.data = from_json(j.at("input")); + + if (const auto it = j.find("gasLimit"); it != j.end()) + o.gas_limit = from_json(*it); + else + o.gas_limit = from_json(j.at("gas")); + o.value = from_json(j.at("value")); if (const auto ac_it = j.find("accessList"); ac_it != j.end()) { o.access_list = from_json(*ac_it); - if (o.kind == state::Transaction::Kind::legacy) // Upgrade tx type if tx has "accessList" - o.kind = state::Transaction::Kind::eip2930; + if (o.type == state::Transaction::Type::legacy) // Upgrade tx type if tx has access list + o.type = state::Transaction::Type::access_list; } if (const auto type_it = j.find("type"); type_it != j.end()) { - if (stdx::to_underlying(o.kind) != from_json(*type_it)) - throw std::invalid_argument("wrong transaction type"); + const auto inferred_type = stdx::to_underlying(o.type); + const auto type = from_json(*type_it); + if (type != inferred_type) + throw std::invalid_argument("wrong transaction type: " + std::to_string(type) + + ", expected: " + std::to_string(inferred_type)); } o.nonce = from_json(j.at("nonce")); @@ -312,10 +388,12 @@ static void from_json(const json::json& j, TestMultiTransaction& o) for (const auto& j_data : j.at("data")) o.inputs.emplace_back(from_json(j_data)); - if (j.contains("accessLists")) + if (const auto ac_it = j.find("accessLists"); ac_it != j.end()) { - for (const auto& j_access_list : j["accessLists"]) + for (const auto& j_access_list : *ac_it) o.access_lists.emplace_back(from_json(j_access_list)); + if (o.type == state::Transaction::Type::legacy) // Upgrade tx type if tx has access lists + o.type = state::Transaction::Type::access_list; } for (const auto& j_gas_limit : j.at("gasLimit")) @@ -340,23 +418,21 @@ static void from_json(const json::json& j, StateTransitionTest::Case::Expectatio o.exception = j.contains("expectException"); } -static void from_json(const json::json& j, StateTransitionTest& o) +static void from_json(const json::json& j_t, StateTransitionTest& o) { - if (!j.is_object() || j.empty()) - throw std::invalid_argument{"JSON test must be an object with single key of the test name"}; - - const auto& j_t = *j.begin(); // Content is in a dict with the test name. - - o.pre_state = from_json(j_t.at("pre")); + o.pre_state = from_json(j_t.at("pre")); o.multi_tx = j_t.at("transaction").get(); o.block = from_json(j_t.at("env")); - if (const auto& info = j_t.at("_info"); info.contains("labels")) + if (const auto info_it = j_t.find("_info"); info_it != j_t.end()) { - for (const auto& [j_id, j_label] : info.at("labels").items()) - o.input_labels.emplace(from_json(j_id), j_label); + if (const auto labels_it = info_it->find("labels"); labels_it != info_it->end()) + { + for (const auto& [j_id, j_label] : labels_it->items()) + o.input_labels.emplace(from_json(j_id), j_label); + } } for (const auto& [rev_name, expectations] : j_t.at("post").items()) @@ -367,20 +443,32 @@ static void from_json(const json::json& j, StateTransitionTest& o) } } -StateTransitionTest load_state_test(std::istream& input) +static void from_json(const json::json& j, std::vector& o) +{ + for (const auto& elem_it : j.items()) + { + auto test = elem_it.value().get(); + test.name = elem_it.key(); + o.emplace_back(std::move(test)); + } +} + +std::vector load_state_tests(std::istream& input) { - return json::json::parse(input).get(); + return json::json::parse(input).get>(); } -void validate_deployed_code(const state::State& state, evmc_revision rev) +void validate_state(const TestState& state, evmc_revision rev) { - for (const auto& [addr, acc] : state.get_accounts()) + for (const auto& [addr, acc] : state) { + // TODO: Check for empty accounts after Paris. + // https://github.com/ethereum/tests/issues/1331 if (is_eof_container(acc.code)) { - if (rev >= EVMC_CANCUN) + if (rev >= EVMC_PRAGUE) { - if (const auto result = validate_eof(rev, acc.code); + if (const auto result = validate_eof(rev, ContainerKind::runtime, acc.code); result != EOFValidationError::success) { throw std::invalid_argument( @@ -390,8 +478,17 @@ void validate_deployed_code(const state::State& state, evmc_revision rev) } else { - throw std::invalid_argument("code at " + hex0x(addr) + " starts with 0xEF00 in " + - evmc_revision_to_string(rev)); + throw std::invalid_argument("unexpected code with EOF prefix at " + hex0x(addr)); + } + } + + for (const auto& [key, value] : acc.storage) + { + if (is_zero(value)) + { + throw std::invalid_argument{"account " + hex0x(addr) + + " contains invalid zero-value storage entry " + + hex0x(key)}; } } } diff --git a/test/statetest/statetest_runner.cpp b/test/statetest/statetest_runner.cpp index f51490dd..daac89f7 100644 --- a/test/statetest/statetest_runner.cpp +++ b/test/statetest/statetest_runner.cpp @@ -9,10 +9,12 @@ namespace evmone::test { -void run_state_test(const StateTransitionTest& test, evmc::VM& vm) +void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_summary) { + SCOPED_TRACE(test.name); for (const auto& [rev, cases] : test.cases) { + validate_state(test.pre_state, rev); for (size_t case_index = 0; case_index != cases.size(); ++case_index) { SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index)); @@ -23,21 +25,45 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm) const auto& expected = cases[case_index]; const auto tx = test.multi_tx.get(expected.indexes); - auto state = test.pre_state; + auto state = test.pre_state.to_intra_state(); - validate_deployed_code(state, rev); - - const auto res = state::transition(state, test.block, tx, rev, vm); + const auto res = state::transition(state, test.block, tx, rev, vm, test.block.gas_limit, + state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); // Finalize block with reward 0. - state::finalize(state, rev, test.block.coinbase, 0, {}); + state::finalize(state, rev, test.block.coinbase, 0, {}, {}); - if (holds_alternative(res)) - EXPECT_EQ(logs_hash(get(res).logs), expected.logs_hash); + const auto state_root = state::mpt_hash(TestState{state}); + + if (trace_summary) + { + std::clog << '{'; + if (holds_alternative(res)) // if tx valid + { + const auto& r = get(res); + if (r.status == EVMC_SUCCESS) + std::clog << R"("pass":true)"; + else + std::clog << R"("pass":false,"error":")" << r.status << '"'; + std::clog << R"(,"gasUsed":"0x)" << std::hex << r.gas_used << R"(",)"; + } + std::clog << R"("stateRoot":"0x)" << hex(state_root) << "\"}\n"; + } + + if (expected.exception) + { + ASSERT_FALSE(holds_alternative(res)) + << "unexpected valid transaction"; + EXPECT_EQ(logs_hash(std::vector()), expected.logs_hash); + } else - EXPECT_TRUE(expected.exception); + { + ASSERT_TRUE(holds_alternative(res)) + << "unexpected invalid transaction: " << get(res).message(); + EXPECT_EQ(logs_hash(get(res).logs), expected.logs_hash); + } - EXPECT_EQ(state::mpt_hash(state.get_accounts()), expected.state_hash); + EXPECT_EQ(state_root, expected.state_hash); } } } diff --git a/test/t8n/CMakeLists.txt b/test/t8n/CMakeLists.txt index b07261cd..5be2dc66 100644 --- a/test/t8n/CMakeLists.txt +++ b/test/t8n/CMakeLists.txt @@ -5,9 +5,6 @@ include(CableBuildInfo) cable_add_buildinfo_library(PROJECT_NAME evmone) -hunter_add_package(nlohmann_json) -find_package(nlohmann_json CONFIG REQUIRED) - add_executable(evmone-t8n) target_link_libraries(evmone-t8n PRIVATE evmone::statetestutils nlohmann_json::nlohmann_json) target_link_libraries(evmone-t8n PRIVATE evmc::evmc evmone evmone-buildinfo) diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 4f267efc..6aca0331 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -2,9 +2,13 @@ // Copyright 2023 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include "../state/errors.hpp" +#include "../state/ethash_difficulty.hpp" #include "../state/mpt_hash.hpp" #include "../state/rlp.hpp" +#include "../state/system_contracts.hpp" #include "../statetest/statetest.hpp" +#include "../utils/utils.hpp" #include #include #include @@ -31,6 +35,7 @@ int main(int argc, const char* argv[]) fs::path output_body_file; std::optional block_reward; uint64_t chain_id = 0; + bool trace = false; try { @@ -38,13 +43,13 @@ int main(int argc, const char* argv[]) { const std::string_view arg{argv[i]}; - if (arg == "-v") + if (arg == "-v" || arg == "--version") { std::cout << "evmone-t8n " EVMONE_VERSION "\n"; return 0; } if (arg == "--state.fork" && ++i < argc) - rev = evmone::test::to_rev(argv[i]); + rev = to_rev(argv[i]); else if (arg == "--input.alloc" && ++i < argc) alloc_file = argv[i]; else if (arg == "--input.env" && ++i < argc) @@ -52,7 +57,10 @@ int main(int argc, const char* argv[]) else if (arg == "--input.txs" && ++i < argc) txs_file = argv[i]; else if (arg == "--output.basedir" && ++i < argc) + { output_dir = argv[i]; + fs::create_directories(output_dir); + } else if (arg == "--output.result" && ++i < argc) output_result_file = argv[i]; else if (arg == "--output.alloc" && ++i < argc) @@ -63,6 +71,8 @@ int main(int argc, const char* argv[]) chain_id = intx::from_string(argv[i]); else if (arg == "--output.body" && ++i < argc) output_body_file = argv[i]; + else if (arg == "--trace") + trace = true; } state::BlockInfo block; @@ -71,7 +81,9 @@ int main(int argc, const char* argv[]) if (!alloc_file.empty()) { const auto j = json::json::parse(std::ifstream{alloc_file}, nullptr, false); - state = test::from_json(j); + const auto test_state = test::from_json(j); + validate_state(test_state, rev); + state = test_state.to_intra_state(); } if (!env_file.empty()) { @@ -80,20 +92,41 @@ int main(int argc, const char* argv[]) } json::json j_result; - // FIXME: Calculate difficulty properly - j_result["currentDifficulty"] = "0x20000"; + + // Difficulty was received from upstream. No need to calc + // TODO: Check if it's needed by the blockchain test. If not remove if statement true branch + if (block.difficulty != 0) + j_result["currentDifficulty"] = hex0x(block.difficulty); + else + { + const auto current_difficulty = state::calculate_difficulty(block.parent_difficulty, + block.parent_ommers_hash != EmptyListHash, block.parent_timestamp, block.timestamp, + block.number, rev); + + j_result["currentDifficulty"] = hex0x(current_difficulty); + block.difficulty = current_difficulty; + + if (rev < EVMC_PARIS) // Override prev_randao with difficulty pre-Merge + block.prev_randao = intx::be::store(intx::uint256{current_difficulty}); + } + j_result["currentBaseFee"] = hex0x(block.base_fee); int64_t cumulative_gas_used = 0; + int64_t blob_gas_left = state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK; std::vector transactions; std::vector receipts; + int64_t block_gas_left = block.gas_limit; // Parse and execute transactions if (!txs_file.empty()) { const auto j_txs = json::json::parse(std::ifstream{txs_file}); - evmc::VM vm{evmc_create_evmone(), {{"O", "0"}}}; + evmc::VM vm{evmc_create_evmone()}; + + if (trace) + vm.set_option("trace", "1"); std::vector txs_logs; @@ -102,14 +135,15 @@ int main(int argc, const char* argv[]) j_result["receipts"] = json::json::array(); j_result["rejected"] = json::json::array(); + state::system_call(state, block, rev, vm); + for (size_t i = 0; i < j_txs.size(); ++i) { auto tx = test::from_json(j_txs[i]); tx.chain_id = chain_id; - auto res = state::transition(state, block, tx, rev, vm); - const auto computed_tx_hash = keccak256(rlp::encode(tx)); + const auto computed_tx_hash_str = hex0x(computed_tx_hash); if (j_txs[i].contains("hash")) { @@ -118,15 +152,32 @@ int main(int argc, const char* argv[]) if (loaded_tx_hash_opt != computed_tx_hash) throw std::logic_error("transaction hash mismatched: computed " + - hex0x(computed_tx_hash) + ", expected " + + computed_tx_hash_str + ", expected " + hex0x(loaded_tx_hash_opt.value())); } + std::ofstream trace_file_output; + const auto orig_clog_buf = std::clog.rdbuf(); + if (trace) + { + const auto output_filename = + output_dir / + ("trace-" + std::to_string(i) + "-" + computed_tx_hash_str + ".jsonl"); + + // `trace` flag enables trace logging to std::clog. + // Redirect std::clog to the output file. + trace_file_output.open(output_filename); + std::clog.rdbuf(trace_file_output.rdbuf()); + } + + auto res = + state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + if (holds_alternative(res)) { const auto ec = std::get(res); json::json j_rejected_tx; - j_rejected_tx["hash"] = hex0x(computed_tx_hash); + j_rejected_tx["hash"] = computed_tx_hash_str; j_rejected_tx["index"] = i; j_rejected_tx["error"] = ec.message(); j_result["rejected"].push_back(j_rejected_tx); @@ -140,9 +191,12 @@ int main(int argc, const char* argv[]) txs_logs.insert(txs_logs.end(), tx_logs.begin(), tx_logs.end()); auto& j_receipt = j_result["receipts"][j_result["receipts"].size()]; - j_receipt["transactionHash"] = hex0x(computed_tx_hash); + j_receipt["transactionHash"] = computed_tx_hash_str; j_receipt["gasUsed"] = hex0x(static_cast(receipt.gas_used)); cumulative_gas_used += receipt.gas_used; + receipt.cumulative_gas_used = cumulative_gas_used; + if (rev < EVMC_BYZANTIUM) + receipt.post_state = state::mpt_hash(TestState{state}); j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used); j_receipt["blockHash"] = hex0x(bytes32{}); @@ -152,46 +206,55 @@ int main(int argc, const char* argv[]) j_receipt["root"] = ""; j_receipt["status"] = "0x1"; j_receipt["transactionIndex"] = hex0x(i); + blob_gas_left -= tx.blob_gas_used(); transactions.emplace_back(std::move(tx)); + block_gas_left -= receipt.gas_used; receipts.emplace_back(std::move(receipt)); } + + // Restore original std::clog buffer (otherwise std::clog crashes at exit). + if (trace) + std::clog.rdbuf(orig_clog_buf); } } - state::finalize(state, rev, block.coinbase, block_reward, block.withdrawals); + state::finalize( + state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); j_result["logsHash"] = hex0x(logs_hash(txs_logs)); - j_result["stateRoot"] = hex0x(state::mpt_hash(state.get_accounts())); + j_result["stateRoot"] = hex0x(state::mpt_hash(TestState{state})); } j_result["logsBloom"] = hex0x(compute_bloom_filter(receipts)); j_result["receiptsRoot"] = hex0x(state::mpt_hash(receipts)); + if (rev >= EVMC_SHANGHAI) + j_result["withdrawalsRoot"] = hex0x(state::mpt_hash(block.withdrawals)); + j_result["txRoot"] = hex0x(state::mpt_hash(transactions)); j_result["gasUsed"] = hex0x(cumulative_gas_used); + if (rev >= EVMC_CANCUN) + { + j_result["blobGasUsed"] = + hex0x(state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK - blob_gas_left); + j_result["currentExcessBlobGas"] = hex0x(block.excess_blob_gas); + } + if (rev >= EVMC_PRAGUE) + { + // EIP-7685: General purpose execution layer requests + j_result["requestsRoot"] = hex0x(state::EMPTY_MPT_HASH); + } std::ofstream{output_dir / output_result_file} << std::setw(2) << j_result; // Print out current state to outAlloc file - json::json j_alloc; - for (const auto& [addr, acc] : state.get_accounts()) - { - j_alloc[hex0x(addr)]["nonce"] = hex0x(acc.nonce); - for (const auto& [key, val] : acc.storage) - if (!is_zero(val.current)) - j_alloc[hex0x(addr)]["storage"][hex0x(key)] = hex0x(val.current); - - j_alloc[hex0x(addr)]["code"] = hex0x(bytes_view(acc.code.data(), acc.code.size())); - j_alloc[hex0x(addr)]["balance"] = hex0x(acc.balance); - } - - std::ofstream{output_dir / output_alloc_file} << std::setw(2) << j_alloc; + std::ofstream{output_dir / output_alloc_file} << std::setw(2) << to_json(TestState{state}); if (!output_body_file.empty()) std::ofstream{output_dir / output_body_file} << hex0x(rlp::encode(transactions)); } catch (const std::exception& e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; return 1; } diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 746066b8..edc599d3 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -7,8 +7,14 @@ add_executable(evmone-unittests) target_sources( evmone-unittests PRIVATE analysis_test.cpp + baseline_analysis_test.cpp + blockchaintest_loader_test.cpp bytecode_test.cpp + eof_validation_stack_test.cpp + eof_example_test.cpp eof_test.cpp + eof_validation.hpp + eof_validation.cpp eof_validation_test.cpp evm_fixture.cpp evm_fixture.hpp @@ -16,11 +22,15 @@ target_sources( evm_calls_test.cpp evm_control_flow_test.cpp evm_eip663_dupn_swapn_test.cpp + evm_eip663_exchange_test.cpp evm_eip2929_test.cpp evm_eip3198_basefee_test.cpp evm_eip3855_push0_test.cpp evm_eip3860_initcode_test.cpp + evm_eip4844_blobhash_test.cpp + evm_eip7516_blobbasefee_test.cpp evm_eof_test.cpp + evm_eof_calls_test.cpp evm_eof_function_test.cpp evm_eof_rjump_test.cpp evm_memory_test.cpp @@ -28,30 +38,66 @@ target_sources( evm_storage_test.cpp evm_other_test.cpp evm_benchmark_test.cpp + evmmax_bn254_add_test.cpp + evmmax_bn254_mul_test.cpp + evmmax_test.cpp + evmmax_secp256k1_test.cpp evmone_test.cpp execution_state_test.cpp + exportable_fixture.hpp + exportable_fixture.cpp instructions_test.cpp + precompiles_blake2b_test.cpp + precompiles_bls_test.cpp + precompiles_kzg_test.cpp + precompiles_ripemd160_test.cpp + precompiles_sha256_test.cpp + state_block_test.cpp state_bloom_filter_test.cpp + state_difficulty_test.cpp state_mpt_hash_test.cpp state_mpt_test.cpp state_new_account_address_test.cpp + state_precompiles_test.cpp state_rlp_test.cpp + state_system_call_test.cpp state_transition.hpp state_transition.cpp state_transition_block_test.cpp + state_transition_call_test.cpp state_transition_create_test.cpp - state_transition_eof_test.cpp + state_transition_eip663_test.cpp + state_transition_eof_calls_test.cpp + state_transition_eof_create_test.cpp + state_transition_extcode_test.cpp + state_transition_selfdestruct_test.cpp + state_transition_touch_test.cpp + state_transition_trace_test.cpp + state_transition_transient_storage_test.cpp + state_transition_tx_test.cpp + state_tx_test.cpp statetest_loader_block_info_test.cpp statetest_loader_test.cpp statetest_loader_tx_test.cpp statetest_logs_hash_test.cpp + statetest_withdrawals_test.cpp tracing_test.cpp eos_evm_test.cpp ) -target_link_libraries(evmone-unittests PRIVATE evmone evmone::state evmone::statetestutils testutils evmc::instructions GTest::gtest GTest::gtest_main) +target_link_libraries(evmone-unittests PRIVATE evmone evmone::evmmax evmone::state evmone::statetestutils testutils evmc::instructions GTest::gtest GTest::gtest_main) target_include_directories(evmone-unittests PRIVATE ${evmone_private_include_dir}) +target_compile_options(evmone-unittests PRIVATE + # Disable false positive C4789 + # https://developercommunity.visualstudio.com/t/False-positive-buffer-overrun-warning-C/10666762 + $<$:-wd4789> +) -gtest_discover_tests(evmone-unittests TEST_PREFIX ${PROJECT_NAME}/unittests/) +gtest_discover_tests( + evmone-unittests + TEST_PREFIX ${PROJECT_NAME}/unittests/ + PROPERTIES + ENVIRONMENT LLVM_PROFILE_FILE=${PROJECT_BINARY_DIR}/unittests-%p.profraw +) option(EVMONE_EVM_TEST_TOOL "Enable EVM unit testing tool for EVMC implementations (not maintained)" OFF) if(EVMONE_EVM_TEST_TOOL) diff --git a/test/unittests/analysis_test.cpp b/test/unittests/analysis_test.cpp index a7b2661c..c282b357 100644 --- a/test/unittests/analysis_test.cpp +++ b/test/unittests/analysis_test.cpp @@ -6,9 +6,9 @@ #include #include #include -#include using namespace evmone::advanced; +using namespace evmone::test; constexpr auto rev = EVMC_BYZANTIUM; const auto& op_tbl = get_op_table(rev); @@ -257,10 +257,11 @@ TEST(analysis, jumpdests_groups) TEST(analysis, example1_eof1) { - const auto code = eof1_bytecode( - push(0x2a) + push(0x1e) + OP_MSTORE8 + OP_MSIZE + push(0) + OP_SSTORE, 2, "deadbeef"); + const bytecode code = + eof_bytecode(push(0x2a) + push(0x1e) + OP_MSTORE8 + OP_MSIZE + push(0) + OP_SSTORE, 2) + .data("deadbeef"); const auto header = evmone::read_valid_eof1_header(code); - const auto analysis = analyze(EVMC_CANCUN, header.get_code(code, 0)); + const auto analysis = analyze(EVMC_PRAGUE, header.get_code(code, 0)); ASSERT_EQ(analysis.instrs.size(), 8); diff --git a/test/unittests/baseline_analysis_test.cpp b/test/unittests/baseline_analysis_test.cpp new file mode 100644 index 00000000..ed3ed118 --- /dev/null +++ b/test/unittests/baseline_analysis_test.cpp @@ -0,0 +1,34 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace evmone::test; + +TEST(baseline_analysis, legacy) +{ + const auto code = push(1) + ret_top(); + const auto analysis = evmone::baseline::analyze(code, false); + + EXPECT_EQ(analysis.eof_header().version, 0); + EXPECT_EQ(analysis.executable_code(), code); + EXPECT_EQ(analysis.raw_code(), code); + EXPECT_NE(analysis.raw_code().data(), code.data()) << "copy should be made"; +} + +TEST(baseline_analysis, eof1) +{ + const auto code = push(1) + ret_top(); + const bytecode container = eof_bytecode(code, 2).data("da4a"); + const auto analysis = evmone::baseline::analyze(container, true); + + EXPECT_EQ(analysis.eof_header().version, 1); + EXPECT_EQ(analysis.eof_header().code_sizes.size(), 1); + EXPECT_EQ(analysis.eof_header().data_size, 2); + EXPECT_EQ(analysis.executable_code(), code); + EXPECT_EQ(analysis.raw_code(), container); + EXPECT_EQ(analysis.raw_code().data(), container.data()) << "copy should not be made"; +} diff --git a/test/unittests/blockchaintest_loader_test.cpp b/test/unittests/blockchaintest_loader_test.cpp new file mode 100644 index 00000000..b4a2e3fb --- /dev/null +++ b/test/unittests/blockchaintest_loader_test.cpp @@ -0,0 +1,380 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace evmone; +using namespace evmone::test; +using namespace testing; + +TEST(json_loader, blockchain_test) +{ + std::istringstream input{R"({ + "000-fork=Shanghai-fill_stack": { + "blocks": [ + { + "blockHeader": { + "parentHash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x07b66de4268c3c26af1346a37fd0bb1584401981aafbedaa7837593030b5f968", + "transactionsTrie": "0x2bad57b8521a8d2a492526aecdb0e1244a14e1bc52809a046ac46a863ed9e54d", + "receiptTrie": "0xc227e1c29620a6496364056ce59ec4f51ed6e7bc56425e213d0195f84544c2c3", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "number": "0x05", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0xbc5f", + "timestamp": "0x03e8", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x0186a0", + "to": "0x0000000000000000000000000000000000000100", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x86ddb9352affa90c20d71652b049404d8abcc6575e8e4a2c0bb9aa73fad9001c", + "s": "0x1bb0d685e5589862ae3d2b083be59c4f754c326800dbc82712e9f81eebf2f61d", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [{ + "baseFeePerGas" : "0x0d", + "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0xb94f5374fce5ed0000000097c15331677e6ebf0b", + "difficulty" : "0x020000", + "extraData" : "0x42", + "gasLimit" : "0x2fefd8", + "gasUsed" : "0x00", + "hash" : "0x83f67e4827f9ec54f3c95d6b0a29e8510838f3ac43d9fdce7f2f5eff57446aba", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce" : "0x0000000000000000", + "number" : "0x03", + "parentHash" : "0xe7728c48165014b3756026376b7d6bf1b185112e0f0b59cf71bffeeda134f002", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0x1d4410100fc542814bcbaee92d805a2b008844408c5d7049bdcc285309e80ca5", + "timestamp" : "0x54c99451", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }], + "withdrawals": [{ + "address" : "0xc000000000000000000000000000000000000001", + "amount" : "0x0186a0", + "index" : "0x00", + "validatorIndex" : "0x00" + }] + } + ], + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x6b1356f9f8d3bf201a9aa2c44f836ab60c92a349385625da90dce80eb6ecad44", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700" + }, + "lastblockhash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531", + "network": "Shanghai", + "pre": { + "0x0000000000000000000000000000000000000100": { + "nonce": "0x00", + "balance": "0x00", + "code": "0x5f", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + } + }, + "postState": { + "0x0000000000000000000000000000000000000100": { + "nonce": "0x00", + "balance": "0x00", + "code": "0x5f", + "storage": { + "0x00": "0x01" + } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "nonce": "0x00", + "balance": "0x02351d", + "code": "0x", + "storage": {} + } + }, + "sealEngine": "NoProof" + } + })"}; + + const auto btt = load_blockchain_tests(input); + + EXPECT_EQ(btt.size(), 1); + EXPECT_EQ(btt[0].test_blocks.size(), 1); + EXPECT_EQ(btt[0].rev.get_revision(0), evmc_revision::EVMC_SHANGHAI); + EXPECT_EQ(btt[0].name, "000-fork=Shanghai-fill_stack"); + EXPECT_EQ(btt[0].genesis_block_header.timestamp, 0); + EXPECT_EQ(btt[0].genesis_block_header.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].genesis_block_header.base_fee_per_gas, 0x07); + + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].type, state::Transaction::Type::legacy); + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].sender, + 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b_address); + EXPECT_EQ(btt[0].test_blocks[0].block_info.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].test_blocks[0].block_info.number, 5); + EXPECT_EQ(btt[0].test_blocks[0].block_info.timestamp, 0x03e8); + EXPECT_EQ(btt[0].test_blocks[0].block_info.ommers.size(), 1); + EXPECT_EQ(btt[0].test_blocks[0].block_info.ommers[0].beneficiary, + 0xb94f5374fce5ed0000000097c15331677e6ebf0b_address); + EXPECT_EQ(btt[0].test_blocks[0].block_info.ommers[0].delta, 2); + EXPECT_EQ(btt[0].test_blocks[0].block_info.withdrawals.size(), 1); + EXPECT_EQ(btt[0].test_blocks[0].block_info.withdrawals[0].recipient, + 0xc000000000000000000000000000000000000001_address); + EXPECT_EQ(btt[0].test_blocks[0].block_info.withdrawals[0].amount_in_gwei, 0x0186a0); + EXPECT_EQ(btt[0].test_blocks[0].block_info.withdrawals[0].index, 0); + EXPECT_EQ(btt[0].test_blocks[0].block_info.withdrawals[0].validator_index, 0); +} + +TEST(json_loader, blockchain_test_post_state_hash) +{ + std::istringstream input{R"({ + "000-fork=Shanghai-fill_stack": { + "blocks": [ + { + "blockHeader": { + "parentHash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x07b66de4268c3c26af1346a37fd0bb1584401981aafbedaa7837593030b5f968", + "transactionsTrie": "0x2bad57b8521a8d2a492526aecdb0e1244a14e1bc52809a046ac46a863ed9e54d", + "receiptTrie": "0xc227e1c29620a6496364056ce59ec4f51ed6e7bc56425e213d0195f84544c2c3", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x05", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0xbc5f", + "timestamp": "0x03e8", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x0186a0", + "to": "0x0000000000000000000000000000000000000100", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x86ddb9352affa90c20d71652b049404d8abcc6575e8e4a2c0bb9aa73fad9001c", + "s": "0x1bb0d685e5589862ae3d2b083be59c4f754c326800dbc82712e9f81eebf2f61d", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [] + } + ], + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x6b1356f9f8d3bf201a9aa2c44f836ab60c92a349385625da90dce80eb6ecad44", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700" + }, + "lastblockhash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531", + "network": "ShanghaiToCancunAtTime15k", + "pre": { + "0x0000000000000000000000000000000000000100": { + "nonce": "0x00", + "balance": "0x00", + "code": "0x5f", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + } + }, + "postStateHash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531", + "sealEngine": "NoProof" + } + })"}; + + const auto btt = load_blockchain_tests(input); + + EXPECT_EQ(btt.size(), 1); + EXPECT_EQ(btt[0].test_blocks.size(), 1); + EXPECT_EQ(btt[0].rev.get_revision(0), evmc_revision::EVMC_SHANGHAI); + EXPECT_EQ(btt[0].rev.get_revision(15'000), evmc_revision::EVMC_CANCUN); + EXPECT_EQ(btt[0].name, "000-fork=Shanghai-fill_stack"); + EXPECT_EQ(btt[0].genesis_block_header.timestamp, 0); + EXPECT_EQ(btt[0].genesis_block_header.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].genesis_block_header.base_fee_per_gas, 0x07); + + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].type, state::Transaction::Type::legacy); + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].sender, + 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b_address); + EXPECT_EQ(btt[0].test_blocks[0].block_info.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].test_blocks[0].block_info.number, 5); + EXPECT_EQ(btt[0].test_blocks[0].block_info.timestamp, 0x03e8); + EXPECT_EQ(std::get(btt[0].expectation.post_state), + 0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531_bytes32); +} + +TEST(json_loader, blockchain_test_pre_paris) +{ + std::istringstream input{R"({ + "000-fork=Shanghai-fill_stack": { + "blocks": [ + { + "blockHeader": { + "parentHash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x07b66de4268c3c26af1346a37fd0bb1584401981aafbedaa7837593030b5f968", + "transactionsTrie": "0x2bad57b8521a8d2a492526aecdb0e1244a14e1bc52809a046ac46a863ed9e54d", + "receiptTrie": "0xc227e1c29620a6496364056ce59ec4f51ed6e7bc56425e213d0195f84544c2c3", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x20000", + "number": "0x05", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0xbc5f", + "timestamp": "0x03e8", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x0186a0", + "to": "0x0000000000000000000000000000000000000100", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x86ddb9352affa90c20d71652b049404d8abcc6575e8e4a2c0bb9aa73fad9001c", + "s": "0x1bb0d685e5589862ae3d2b083be59c4f754c326800dbc82712e9f81eebf2f61d", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [] + } + ], + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x6b1356f9f8d3bf201a9aa2c44f836ab60c92a349385625da90dce80eb6ecad44", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0xe1bcc830589216abdc79cb3075f06f7b133f7b0cf257ecb346da33c354099700" + }, + "lastblockhash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531", + "network": "London", + "pre": { + "0x0000000000000000000000000000000000000100": { + "nonce": "0x00", + "balance": "0x00", + "code": "0x5f", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + } + }, + "postStateHash": "0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531", + "sealEngine": "NoProof" + } + })"}; + + const auto btt = load_blockchain_tests(input); + + EXPECT_EQ(btt.size(), 1); + EXPECT_EQ(btt[0].test_blocks.size(), 1); + EXPECT_EQ(btt[0].rev.get_revision(0), evmc_revision::EVMC_LONDON); + EXPECT_EQ(btt[0].name, "000-fork=Shanghai-fill_stack"); + EXPECT_EQ(btt[0].genesis_block_header.timestamp, 0); + EXPECT_EQ(btt[0].genesis_block_header.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].genesis_block_header.base_fee_per_gas, 0x07); + + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].type, state::Transaction::Type::legacy); + EXPECT_EQ(btt[0].test_blocks[0].transactions[0].sender, + 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b_address); + EXPECT_EQ(btt[0].test_blocks[0].block_info.gas_limit, 0x016345785d8a0000); + EXPECT_EQ(btt[0].test_blocks[0].block_info.number, 5); + EXPECT_EQ(btt[0].test_blocks[0].block_info.timestamp, 0x03e8); + EXPECT_EQ(std::get(btt[0].expectation.post_state), + 0x01de610f00331cea813e8143d51eb44ca352cdd90c602bb4b4bcf3c6cf9d5531_bytes32); + EXPECT_EQ(btt[0].test_blocks[0].block_info.prev_randao, + 0x0000000000000000000000000000000000000000000000000000000000020000_bytes32); +} diff --git a/test/unittests/bytecode_test.cpp b/test/unittests/bytecode_test.cpp index 85937739..ca49bf89 100644 --- a/test/unittests/bytecode_test.cpp +++ b/test/unittests/bytecode_test.cpp @@ -5,6 +5,8 @@ #include #include +using namespace evmone::test; + TEST(bytecode, push) { auto code = push("0102") + OP_POP + push("010203040506070809") + "50"; diff --git a/test/unittests/eof_example_test.cpp b/test/unittests/eof_example_test.cpp new file mode 100644 index 00000000..7e25d537 --- /dev/null +++ b/test/unittests/eof_example_test.cpp @@ -0,0 +1,259 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +// This test file is serving as a set of examples of basic valid EOF codes, +// to be linked to from external specs and documentation. + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, eof_examples_minimal) +{ + // # Example 1 + // + // A minimal valid EOF container doing nothing. + + rev = EVMC_PRAGUE; + + const auto eof_code = bytecode( + // Code section: STOP + // Header: 1 code section 1 byte long | + // | | + // version | Header terminator + // | |___________ | | + "EF00 01 01 0004 02 0001 0001 04 0000 00 00 80 0000 00" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | Header: data section 0 bytes long + // | | + // Header: types section 4 bytes long + // | + // Types section: first code section 0 inputs, + // non-returning, max stack height 0 + ); + + // Tests the code is valid EOF and does nothing. + tx.to = To; + pre.insert(*tx.to, { + .code = eof_code, + }); + + expect.post[*tx.to].exists = true; +} + +TEST_F(state_transition, eof_examples_static_relative_jump_loop) +{ + // # Example 2 + // + // EOF container looping infinitely using the static relative jump instruction RJUMP. + + rev = EVMC_PRAGUE; + + const auto eof_code = bytecode( + // Code section: RJUMP back to start (-3) + // - infinite loop + // | + // Header: 1 code section 3 bytes long + // | | + // version | Header terminator + // | |___________ | | + "EF00 01 01 0004 02 0001 0003 04 0000 00 00 80 0000 E0FFFD" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | Header: data section 0 bytes long + // | | + // Header: types section 4 bytes long + // | + // Types section: first code section 0 inputs, + // non-returning, max stack height 0 + ); + + // Tests the code is valid EOF and the infinite loop runs out of gas. + tx.to = To; + pre.insert(*tx.to, { + .code = eof_code, + }); + + expect.status = EVMC_OUT_OF_GAS; + expect.post[*tx.to].exists = true; +} + +TEST_F(state_transition, eof_examples_callf) +{ + // # Example 3 + // + // EOF container with two code sections, one calling the other passing a single argument on the + // stack and retrieving the same single value back from the stack on return. + + rev = EVMC_PRAGUE; + + const auto eof_code = bytecode( + // First code section: PUSH1(0x2A), + // CALLF second section and STOP + // Header: 2 code sections: | + // | - first code section 6 bytes long | Second code section: + // | - second code section 1 byte long | return the input + // | | | + // version | Header terminator | | + // | |________________ | |_____________ | + "EF00 01 01 0008 02 0002 0006 0001 04 0000 00 00 80 0001 01 01 0001 602A E30001 00 E4" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + // | Header: data section 0 bytes long + // | | + // Header: types section 8 bytes long | + // | + // Types section: first code section 0 inputs, + // non-returning, max stack height 1; + // second code section 1 input, + // 1 output, max stack height 1 + ); + + // Tests the code is valid EOF. + tx.to = To; + pre.insert(*tx.to, { + .code = eof_code, + }); + + expect.post[*tx.to].exists = true; +} + +TEST_F(state_transition, eof_examples_creation_tx) +{ + // # Example 4 + // + // A creation transaction used to create a new EOF contract. + + rev = EVMC_PRAGUE; + + const auto initcontainer = bytecode( + ////////////////// + // Initcontainer + // Code section: PUSH0 [aux data size], PUSH0 [aux data offset] and + // RETURNCONTRACT first subcontainer + // | + // Header: 1 code section 4 bytes long | + // | | + // version | Header terminator + // | |___________ | |________ + "EF00 01 01 0004 02 0001 0004 03 0001 0014 04 0000 00 00 80 0002 5F5F EE00" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | | Header: data section 0 bytes long + // | | | + // Header: types section 4 bytes long Types section: first code section + // | 0 inputs, non-returning, + // | max stack height 2 + // Header: 1 subcontainer 20 bytes long + // + ////////////////// + // Deployed container (contract doing nothing, see Example 1) + "EF00 01 01 0004 02 0001 0001 04 0000 00 00 80 0000 00"); + + // Put the initcontainer in the `data` field of the transaction, appending some calldata. + tx.data = initcontainer + "ABCDEF"; + // Empty `to` field. + tx.to = std::nullopt; + + // Address of the newly created contract is calculated using the deployer's address and nonce. + expect.post[0x3442a1dec1e72f337007125aa67221498cdd759d_address].exists = true; +} + +TEST_F(state_transition, eof_examples_eofcreate) +{ + // # Example 5 + // + // A factory contract with an EOFCREATE instruction is being called in order + // to deploy its subcontainer as a new EOF contract. + + rev = EVMC_PRAGUE; + + const auto factory = bytecode( + ////////////////// + // Factory container + // Code section: PUSH0 [input size], PUSH0 [input offset], PUSH1 [salt], + // PUSH0 [endowment value], + // EOFCREATE from first subcontainer and STOP + // | + // Header: 1 code section 8 bytes long | + // | | + // version | Header terminator + // | |___________ | |____________________ + "EF00 01 01 0004 02 0001 0008 03 0001 0030 04 0000 00 00 80 0004 5F 5F 60FF 5F EC00 00" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | | Header: data section 0 bytes long + // | | | + // Header: types section 4 bytes long Types section: first code section + // | 0 inputs, non-returning, + // | max stack height 4 + // Header: 1 subcontainer 48 bytes long + // + ////////////////// + // Initcontainer + // Code section: PUSH0 [aux data size], PUSH0 [aux data offset], + // RETURNCONTRACT first subcontainer + // | + // Header: 1 code section 8 bytes long | + // | | + // version | Header terminator + // | |___________ | |_________ + "EF00 01 01 0004 02 0001 0004 03 0001 0014 04 0000 00 00 80 0002 5F 5F EE00" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | | Header: data section 0 bytes long + // | | | + // Header: types section 4 bytes long Types section: first code section + // | 0 inputs, non-returning, + // | max stack height 2 + // Header: 1 subcontainer 20 bytes long + // + ////////////////// + // Deployed container (contract doing nothing, see Example 1) + "EF00 01 01 0004 02 0001 0001 04 0000 00 00 80 0000 00"); + + // Tests the code is valid EOF and when called with initcodes creates a new contract. + tx.to = To; + pre.insert(*tx.to, { + .code = factory, + }); + + // Address of the newly created contract is calculated using the salt, initcontainer hash and + // deployer address. + expect.post[0x5ea44d9b32a04ae2c15fe4fa8ebf9a8a5a1e7e89_address].exists = true; + expect.post[*tx.to].exists = true; +} + +TEST_F(state_transition, eof_examples_data) +{ + // # Example 6 + // + // A basic EOF contract with a data section being used to load a byte of data onto the stack. + + rev = EVMC_PRAGUE; + + // clang-format off + const auto eof_code = bytecode( + // Code section: DATALOADN onto the stack the first word of data, STOP + // | + // Header: 1 code section 4 bytes long + // | | + // version | Header terminator Data section + // | |___________ | |________ |_________________________________________________________________ + "EF00 01 01 0004 02 0001 0004 04 0021 00 00 80 0001 D10000 00 454F462068617320736F6D65206772656174206578616D706C6573206865726521" + // |‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ + // | Header: data section 33 bytes long + // | | + // Header: types section 4 bytes long + // | + // Types section: first code section 0 inputs, + // non-returning, max stack height 1 + ); + // clang-format on + + // Tests the code is valid EOF and does nothing. + tx.to = To; + pre.insert(*tx.to, { + .code = eof_code, + }); + + expect.post[*tx.to].exists = true; +} diff --git a/test/unittests/eof_test.cpp b/test/unittests/eof_test.cpp index 67336431..a7aa0392 100644 --- a/test/unittests/eof_test.cpp +++ b/test/unittests/eof_test.cpp @@ -4,9 +4,11 @@ #include #include +#include #include using namespace evmone; +using namespace evmone::test; TEST(eof, is_eof_container) { @@ -30,25 +32,86 @@ TEST(eof, read_valid_eof1_header) { std::string code; uint16_t types_size; - uint16_t code_size; uint16_t data_size; + std::vector code_sizes; + std::vector container_sizes; }; + + std::string code_sections_256; + for (int i = 0; i < 255; ++i) + code_sections_256 += "E5" + hex(big_endian(static_cast(i + 1))); + code_sections_256 += "5B5B00"; + + std::string eofcreate_256; + for (int i = 0; i < 256; ++i) + eofcreate_256 += "5F5F5F5FEC" + hex(static_cast(i)) + "50"; + eofcreate_256 += "00"; + const auto eofcreate_256_size = static_cast(eofcreate_256.size() / 2); + const TestCase test_cases[] = { - {"EF00 01 010004 0200010001 030000 00 00000000 00", 4, 1, 0}, - {"EF00 01 010004 0200010006 030000 00 00000400 600160005500", 4, 6, 0}, - {"EF00 01 010004 0200010001 030001 00 00000000 00 00 AA", 4, 1, 1}, - {"EF00 01 010004 0200010006 030004 00 00000000 600160005500 AABBCCDD", 4, 6, 4}, - {"EF00 01 010004 0200010100 031000 00 00000000" + std::string(256, '0') + - std::string(4096, 'F'), - 4, 256, 4096}, + {"EF00 01 010004 0200010001 040000 00 00800000 00", 4, 0, {1}, {}}, + {"EF00 01 010004 0200010006 040000 00 00800002 600160005500", 4, 0, {6}, {}}, + {"EF00 01 010004 0200010001 040001 00 00800000 00 AA", 4, 1, {1}, {}}, + {"EF00 01 010004 0200010006 040004 00 00800002 600160005500 AABBCCDD", 4, 4, {6}, {}}, + {"EF00 01 01000C 020003000300030003 040000 00 008000000080000000800000 E50001 E50002 " + "5B5B00", + 12, 0, {3, 3, 3}, {}}, + {"EF00 01 01000C 020003000300030003 040004 00 008000000080000000800000 E50001 E50002 " + "5B5B00 FFFFFFFF", + 12, 4, {3, 3, 3}, {}}, + {"EF00 01 010004 0200010100 041000 00 00800000" + hex(255 * bytecode("5B")) + "00" + + std::string(8192, 'F'), + 4, 4096, {256}, {}}, + {"EF00 01 010400 020100" + hex(256 * bytecode("0003")) + " 041000 00 " + + hex(256 * bytecode("00800000")) + code_sections_256 + std::string(8192, 'F'), + 4 * 256, 4096, std::vector(256, 3), {}}, + {"EF00 01 010004 0200010007 0300010014 040000 00 00800004 5F5F5F5FEC0000 " + "EF000101000402000100010400000000800000FE", + 4, 0, {7}, {20}}, + {"EF00 01 010004 0200010015 030003001400160018 040000 00 00800004 " + "5F5F5F5FEC00505F5F5F5FEC01505F5F5F5FEC0200 " + "EF000101000402000100010400000000800000FE EF0001010004020001000304000000008000025F5FFD " + "EF00010100040200010005040000000080000260015F55FE", + 4, 0, {21}, {20, 22, 24}}, + {"EF00 01 010004 0200010015 030003001400160018 040003 00 00800004 " + "5F5F5F5FEC00505F5F5F5FEC01505F5F5F5FEC0200 " + "EF000101000402000100010400000000800000FE EF0001010004020001000304000000008000025F5FFD " + "EF00010100040200010005040000000080000260015F55FE ddeeff", + 4, 3, {21}, {20, 22, 24}}, + {"EF00 01 01000C 020003000300030015 030003001400160018 040003 00 008000000080000000800004 " + "E50001 E50002 5F5F5F5FEC00505F5F5F5FEC01505F5F5F5FEC0200 " + "EF000101000402000100010400000000800000FE " + "EF0001010004020001000304000000008000025F5FFD " + "EF00010100040200010005040000000080000260015F55FE ddeeff", + 12, 3, {3, 3, 21}, {20, 22, 24}}, + {"EF00 01 010004 020001" + hex(big_endian(eofcreate_256_size)) + "030100" + + hex(256 * bytecode("0014")) + "040000 00 00800004" + eofcreate_256 + + hex(256 * bytecode("EF000101000402000100010400000000800000FE")), + 4, 0, {eofcreate_256_size}, std::vector(256, 20)}, }; for (const auto& test_case : test_cases) { const auto code = from_spaced_hex(test_case.code).value(); + EXPECT_EQ( + validate_eof(EVMC_PRAGUE, ContainerKind::runtime, code), EOFValidationError::success) + << test_case.code; + const auto header = read_valid_eof1_header(code); - EXPECT_EQ(header.code_sizes[0], test_case.code_size) << test_case.code; + EXPECT_EQ(header.code_sizes, test_case.code_sizes) << test_case.code; EXPECT_EQ(header.data_size, test_case.data_size) << test_case.code; EXPECT_EQ(header.types.size() * 4, test_case.types_size) << test_case.code; + EXPECT_EQ(header.container_sizes, test_case.container_sizes) << test_case.code; } } + +TEST(eof, get_error_message) +{ + EXPECT_EQ(evmone::get_error_message(EOFValidationError::success), "success"); + EXPECT_EQ(evmone::get_error_message(EOFValidationError::invalid_prefix), "invalid_prefix"); + EXPECT_EQ(evmone::get_error_message(EOFValidationError::stack_overflow), "stack_overflow"); + EXPECT_EQ(evmone::get_error_message(EOFValidationError::impossible), "impossible"); + + // NOLINTNEXTLINE(*.EnumCastOutOfRange) + EXPECT_EQ(evmone::get_error_message(static_cast(-1)), ""); +} diff --git a/test/unittests/eof_validation.cpp b/test/unittests/eof_validation.cpp new file mode 100644 index 00000000..83ba5ad5 --- /dev/null +++ b/test/unittests/eof_validation.cpp @@ -0,0 +1,160 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "eof_validation.hpp" +#include +#include +#include + +namespace evmone::test +{ +namespace +{ +std::string_view get_tests_error_message(EOFValidationError err) noexcept +{ + switch (err) + { + case EOFValidationError::success: + return "success"; + case EOFValidationError::invalid_prefix: + return "EOF_InvalidPrefix"; + case EOFValidationError::eof_version_unknown: + return "EOF_UnknownVersion"; + case EOFValidationError::incomplete_section_size: + return "EOF_IncompleteSectionSize"; + case EOFValidationError::incomplete_section_number: + return "EOF_IncompleteSectionNumber"; + case EOFValidationError::header_terminator_missing: + return "EOF_HeaderTerminatorMissing"; + case EOFValidationError::type_section_missing: + return "EOF_TypeSectionMissing"; + case EOFValidationError::code_section_missing: + return "EOF_CodeSectionMissing"; + case EOFValidationError::data_section_missing: + return "EOF_DataSectionMissing"; + case EOFValidationError::zero_section_size: + return "EOF_ZeroSectionSize"; + case EOFValidationError::section_headers_not_terminated: + return "EOF_SectionHeadersNotTerminated"; + case EOFValidationError::invalid_section_bodies_size: + return "EOF_InvalidSectionBodiesSize"; + case EOFValidationError::unreachable_code_sections: + return "EOF_UnreachableCodeSections"; + case EOFValidationError::undefined_instruction: + return "EOF_UndefinedInstruction"; + case EOFValidationError::truncated_instruction: + return "EOF_TruncatedImmediate"; + case EOFValidationError::invalid_rjump_destination: + return "EOF_InvalidJumpDestination"; + case EOFValidationError::too_many_code_sections: + return "EOF_TooManyCodeSections"; + case EOFValidationError::invalid_type_section_size: + return "EOF_InvalidTypeSectionSize"; + case EOFValidationError::invalid_first_section_type: + return "EOF_InvalidFirstSectionType"; + case EOFValidationError::invalid_max_stack_height: + return "EOF_InvalidMaxStackHeight"; + case EOFValidationError::max_stack_height_above_limit: + return "EOF_MaxStackHeightExceeded"; + case EOFValidationError::inputs_outputs_num_above_limit: + return "EOF_InputsOutputsNumAboveLimit"; + case EOFValidationError::no_terminating_instruction: + return "EOF_InvalidCodeTermination"; + case EOFValidationError::stack_height_mismatch: + return "EOF_ConflictingStackHeight"; + case EOFValidationError::stack_higher_than_outputs_required: + return "EOF_InvalidNumberOfOutputs"; + case EOFValidationError::unreachable_instructions: + return "EOF_UnreachableCode"; + case EOFValidationError::stack_underflow: + return "EOF_StackUnderflow"; + case EOFValidationError::stack_overflow: + return "EOF_StackOverflow"; + case EOFValidationError::invalid_code_section_index: + return "EOF_InvalidCodeSectionIndex"; + case EOFValidationError::invalid_dataloadn_index: + return "EOF_InvalidDataloadnIndex"; + case EOFValidationError::jumpf_destination_incompatible_outputs: + return "EOF_JumpfDestinationIncompatibleOutputs"; + case EOFValidationError::invalid_non_returning_flag: + return "EOF_InvalidNonReturningFlag"; + case EOFValidationError::callf_to_non_returning_function: + return "EOF_CallfToNonReturningFunction"; + case EOFValidationError::too_many_container_sections: + return "EOF_TooManyContainerSections"; + case EOFValidationError::invalid_container_section_index: + return "EOF_InvalidContainerSectionIndex"; + case EOFValidationError::eofcreate_with_truncated_container: + return "EOF_EofCreateWithTruncatedContainer"; + case EOFValidationError::toplevel_container_truncated: + return "EOF_ToplevelContainerTruncated"; + case EOFValidationError::ambiguous_container_kind: + return "EOF_AmbiguousContainerKind"; + case EOFValidationError::incompatible_container_kind: + return "EOF_IncompatibleContainerKind"; + case EOFValidationError::container_size_above_limit: + return "EOF_ContainerSizeAboveLimit"; + case EOFValidationError::unreferenced_subcontainer: + return "EOF_UnreferencedSubcontainer"; + case EOFValidationError::impossible: + return "impossible"; + } + return ""; +} + +} // namespace + +void eof_validation::TearDown() +{ + for (size_t i = 0; i < test_cases.size(); ++i) + { + const auto& test_case = test_cases[i]; + + // Move the container to new heap-allocated buffer of exact size + // to easily find out-of-buffer reads with address sanitizer. + const auto size = test_case.container.size(); + const auto buffer = std::make_unique_for_overwrite(size); + std::ranges::copy(test_case.container, buffer.get()); + const bytes_view container{buffer.get(), size}; + + EXPECT_EQ(evmone::validate_eof(rev, test_case.kind, container), test_case.error) + << "test case " << i << " " << test_case.name << "\n" + << hex(container); + } + + if (!export_file_path.empty()) + export_eof_validation_test(); +} + +void eof_validation::export_eof_validation_test() +{ + json::json j; + auto& jt = j[export_test_name]; + + auto& jvectors = jt["vectors"]; + for (size_t i = 0; i < test_cases.size(); ++i) + { + const auto& test_case = test_cases[i]; + const auto case_name = test_case.name.empty() ? + (std::string{export_test_name} + "_" + std::to_string(i)) : + test_case.name; + + auto& jcase = jvectors[case_name]; + jcase["code"] = hex0x(test_case.container); + if (test_case.kind == ContainerKind::initcode) + jcase["containerKind"] = "INITCODE"; + + auto& jresults = jcase["results"][evmc::to_string(rev)]; + if (test_case.error == EOFValidationError::success) + jresults["result"] = true; + else + { + jresults["result"] = false; + jresults["exception"] = get_tests_error_message(test_case.error); + } + } + + std::ofstream{export_file_path} << std::setw(2) << j; +} +} // namespace evmone::test diff --git a/test/unittests/eof_validation.hpp b/test/unittests/eof_validation.hpp new file mode 100644 index 00000000..d467a46e --- /dev/null +++ b/test/unittests/eof_validation.hpp @@ -0,0 +1,64 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include "exportable_fixture.hpp" +#include +#include +#include +#include +#include + +namespace evmone::test +{ +using evmc::bytes; + +/// Fixture for defining test cases for EOF validation. +/// +/// Each test contains multiple cases, which are validated during test teardown. +class eof_validation : public ExportableFixture +{ +protected: + /// EOF validation test case. + struct TestCase + { + /// Container to be validated. + bytes container; + /// Expected container kind + ContainerKind kind = ContainerKind::runtime; + /// Expected error if container is expected to be invalid, + /// or EOFValidationError::success if it is expected to be valid. + EOFValidationError error = EOFValidationError::success; + /// (Optional) Test case description. + /// In non-empty, exported test file will use it for test case name. + std::string name; + }; + + evmc_revision rev = EVMC_PRAGUE; + std::vector test_cases; + + /// Adds the case to test cases. + /// + /// Can be called as add_test_case(string_view hex, error, name) + /// or add_test_case(bytes_view cont, error, name). + void add_test_case(bytecode container, EOFValidationError error, std::string name = {}) + { + test_cases.push_back( + {std::move(container), ContainerKind::runtime, error, std::move(name)}); + } + + void add_test_case( + bytecode container, ContainerKind kind, EOFValidationError error, std::string name = {}) + { + test_cases.push_back({std::move(container), kind, error, std::move(name)}); + } + + /// The test runner. + void TearDown() override; + + /// Exports the test in the JSON EOF Test format in the given directory. + void export_eof_validation_test(); +}; + +} // namespace evmone::test diff --git a/test/unittests/eof_validation_stack_test.cpp b/test/unittests/eof_validation_stack_test.cpp new file mode 100644 index 00000000..14b8ead9 --- /dev/null +++ b/test/unittests/eof_validation_stack_test.cpp @@ -0,0 +1,1487 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "eof_validation.hpp" +#include +#include +#include +#include + +using namespace evmone; +using namespace evmone::test; + +namespace +{ +// code prologue that creates a segment starting with possible stack heights 1 and 3 +const auto varstack = push0() + rjumpi(2, 0) + push0() + push0(); +} // namespace + +TEST_F(eof_validation, unreachable_instructions) +{ + add_test_case( + eof_bytecode(bytecode{OP_STOP} + OP_STOP), EOFValidationError::unreachable_instructions); + + add_test_case( + eof_bytecode(rjump(1) + OP_STOP + OP_STOP), EOFValidationError::unreachable_instructions); + + // STOP reachable only via backwards jump - invalid + add_test_case( + eof_bytecode(rjump(1) + OP_STOP + rjump(-4)), EOFValidationError::unreachable_instructions); +} + +TEST_F(eof_validation, no_terminating_instruction) +{ + add_test_case(eof_bytecode(push0()), EOFValidationError::no_terminating_instruction); + + add_test_case(eof_bytecode(add(1, 2)), EOFValidationError::no_terminating_instruction); + + add_test_case(eof_bytecode(rjumpi(-5, 1)), EOFValidationError::no_terminating_instruction); +} + +TEST_F(eof_validation, non_constant_stack_height) +{ + // Final "OP_PUSH0 + OP_PUSH0 + OP_REVERT" can be reached with stack heights: 0, 2 or 1 + add_test_case(eof_bytecode(rjumpi(7, OP_PUSH0) + OP_PUSH0 + OP_PUSH0 + rjumpi(1, OP_PUSH0) + + OP_POP + OP_PUSH0 + OP_PUSH0 + OP_REVERT, + 4), + EOFValidationError::success); + + // Final "OP_POP + OP_PUSH0 + OP_PUSH0 + OP_REVERT" can be reached with stack heights: 1, 3 or 2 + add_test_case(eof_bytecode(push0() + rjumpi(7, OP_PUSH0) + OP_PUSH0 + OP_PUSH0 + + rjumpi(1, OP_PUSH0) + OP_POP + OP_PUSH0 + OP_PUSH0 + OP_REVERT, + 5), + EOFValidationError::success); + + // Final "OP_POP + OP_POP + OP_PUSH0 + OP_PUSH0 + OP_REVERT" can be reached with stack heights: + // 0, 2 or 1. Stack underflow when height is 0. + add_test_case(eof_bytecode(rjumpi(7, OP_PUSH0) + OP_PUSH0 + OP_PUSH0 + rjumpi(1, OP_PUSH0) + + OP_POP + OP_POP + OP_PUSH0 + OP_PUSH0 + OP_REVERT, + 4), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, stack_range_maximally_broad) +{ + // Construct series of RJUMPIs all targeting final STOP. + // Stack range at STOP is [0, 1023] + bytecode code = OP_STOP; + int16_t offset = 1; + for (auto i = 0; i < 1023; ++i) + { + code = rjumpi(offset, OP_PUSH0) + OP_PUSH0 + code; + offset += 5; + } + + add_test_case(eof_bytecode(code, 1023), EOFValidationError::success, "valid_1023_rjumpis"); + + code = rjumpi(offset, OP_PUSH0) + OP_PUSH0 + code; + add_test_case(eof_bytecode(code, 1023), EOFValidationError::invalid_max_stack_height, + "invalid_1024_rjumpis"); +} + +TEST_F(eof_validation, backwards_rjump) +{ + add_test_case(eof_bytecode(rjump(-3)), EOFValidationError::success); + + add_test_case(eof_bytecode(push0() + OP_POP + rjump(-5), 1), EOFValidationError::success); + + // rjump backwards from different locations to the same target + add_test_case(eof_bytecode(push0() + OP_POP + rjumpi(3, 1) + rjump(-8) + rjump(-11), 1), + EOFValidationError::success); + + // rjump backwards from different locations to the same target - stack height mismatch + add_test_case( + eof_bytecode(push0() + OP_POP + rjumpi(3, 1) + rjump(-8) + OP_PUSH0 + rjump(-12), 1), + EOFValidationError::stack_height_mismatch); + + // infinite pushing loop + add_test_case(eof_bytecode(push0() + rjump(-4), 1), EOFValidationError::stack_height_mismatch); + + // infinite popping loop + add_test_case( + eof_bytecode(push0() + OP_POP + rjump(-4), 1), EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, backwards_rjump_variable_stack) +{ + add_test_case(eof_bytecode(varstack + rjump(-3), 3), EOFValidationError::success); + + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + OP_POP + rjump(-5), 4), EOFValidationError::success); + + // rjump backwards from different locations to the same target + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + OP_POP + rjumpi(3, 1) + rjump(-8) + rjump(-11), 4), + EOFValidationError::success); + + // rjump backwards from different locations to the same target - stack height mismatch + // 1st rjump: stack [1, 3] + // 2nd rjump: stack [2, 4] + // Jumping to [1, 3] + add_test_case( + eof_bytecode( + varstack + OP_PUSH0 + OP_POP + rjumpi(3, 1) + rjump(-8) + OP_PUSH0 + rjump(-12), 4), + EOFValidationError::stack_height_mismatch); + + // rjump backwards - max stack height mismatch + // rjumpi: stack [1, 3] + // push0: stack [2, 4] + // rjump: stack [1, 4] + // Jumping from [1, 4] to [1, 3] + add_test_case(eof_bytecode(varstack + rjumpi(1, 0) + OP_PUSH0 + rjump(-7), 4), + EOFValidationError::stack_height_mismatch); + + // rjump backwards - min stack height mismatch + // rjumpi: stack [1, 3] + // pop : stack [0, 2] + // rjump: stack [0, 3] + // Jumping from [0, 3] to [1, 3] + add_test_case(eof_bytecode(varstack + rjumpi(1, 0) + OP_POP + rjump(-7), 4), + EOFValidationError::stack_height_mismatch); + + // infinite pushing loop + add_test_case( + eof_bytecode(varstack + push0() + rjump(-4), 4), EOFValidationError::stack_height_mismatch); + + // infinite popping loop + add_test_case(eof_bytecode(varstack + push0() + OP_POP + rjump(-4), 3), + EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, forwards_rjump) +{ + add_test_case(eof_bytecode(rjump(0) + OP_STOP), EOFValidationError::success); + + // forwards rjump + fallthrough - equal stack + add_test_case(eof_bytecode(push0() + rjumpi(3, 0) + rjump(1) + OP_NOT + OP_STOP, 2), + EOFValidationError::success); + + // forwards rjump + forwards rjump + fallthrough - equal stack + add_test_case( + eof_bytecode( + push0() + rjumpi(8, 0) + rjumpi(6, 0) + rjump(4) + rjump(1) + OP_NOT + OP_STOP, 2), + EOFValidationError::success); + + // forwards rjump + fallthrough - different stack + // rjump: [1, 1] + // push0: [2, 2] + add_test_case(eof_bytecode(push0() + rjumpi(3, 0) + rjump(1) + OP_PUSH0 + OP_STOP, 2), + EOFValidationError::success); + + // forwards rjump + forwards rjump + fallthrough - different stack + add_test_case(eof_bytecode(push0() + rjumpi(8, 0) + // [1, 1] + rjumpi(7, 0) + // [1, 1] + rjump(5) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjump(1) + // [2, 2] + OP_NOT + // [1, 1] + OP_STOP, // [1, 2] + 2), + EOFValidationError::success); +} + +TEST_F(eof_validation, forwards_rjump_variable_stack) +{ + add_test_case(eof_bytecode(varstack + rjump(0) + OP_STOP, 3), EOFValidationError::success); + + // forwards rjump + fallthrough - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + rjumpi(3, 0) + rjump(1) + OP_NOT + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjump + forwards rjump + fallthrough - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + rjumpi(8, 0) + rjumpi(6, 0) + rjump(4) + + rjump(1) + OP_NOT + OP_STOP, + 5), + EOFValidationError::success); + + // forwards rjump + fallthrough - different stack + add_test_case(eof_bytecode(varstack + // [1, 3] + OP_PUSH0 + // [2, 4] + rjumpi(3, 0) + // [2, 4] + rjump(1) + // [2, 4] + OP_PUSH0 + // [3, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::success); + + // forwards rjump + forwards rjump + fallthrough - different stack + add_test_case(eof_bytecode(varstack + rjumpi(8, 0) + // [1, 3] + rjumpi(7, 0) + // [1, 3] + rjump(5) + // [1, 3] + OP_PUSH0 + // [2, 4] + rjump(1) + // [2, 4] + OP_NOT + // [1, 3] + OP_STOP, // [1, 4] + 4), + EOFValidationError::success); +} + +TEST_F(eof_validation, backwards_rjumpi) +{ + add_test_case(eof_bytecode(rjumpi(-5, 0) + OP_STOP, 1), EOFValidationError::success); + + add_test_case( + eof_bytecode(push0() + OP_POP + rjumpi(-7, 0) + OP_STOP, 1), EOFValidationError::success); + + // rjumpi backwards from different locations to the same target + add_test_case(eof_bytecode(push0() + OP_POP + rjumpi(-7, 0) + rjumpi(-12, 0) + OP_STOP, 1), + EOFValidationError::success); + + // rjumpi backwards from different locations to the same target - stack height mismatch + add_test_case( + eof_bytecode(push0() + OP_POP + rjumpi(-7, 0) + OP_PUSH0 + rjumpi(-13, 0) + OP_STOP, 2), + EOFValidationError::stack_height_mismatch); + + // valid loop + add_test_case(eof_bytecode(push0() + push(1) + OP_ADD + rjumpi(-7, OP_DUP1) + OP_STOP, 2), + EOFValidationError::success); + + // pushing loop + add_test_case( + eof_bytecode(push0() + push(1) + OP_ADD + OP_DUP1 + rjumpi(-8, OP_DUP1) + OP_STOP, 2), + EOFValidationError::stack_height_mismatch); + + // popping loop + add_test_case(eof_bytecode(push0() + OP_PUSH0 + OP_PUSH0 + rjumpi(-4, OP_POP) + OP_STOP, 2), + EOFValidationError::stack_height_mismatch); + + // rjump and rjumpi with the same target - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + OP_POP + // [0, 0] + rjumpi(-7, 0) + // [1, 1] + rjump(-10), // [1, 1] + 1), + EOFValidationError::success); + + // rjump and rjumpi with the same target - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + OP_POP + // [0, 0] + rjumpi(-7, 0) + // [0, 0] + OP_PUSH0 + // [1, 1] + rjump(-11), // [1, 1] + 1), + EOFValidationError::stack_height_mismatch); + + // rjumpi backwards - only max stack height mismatch + add_test_case(eof_bytecode(OP_PUSH0 + // [1, 1] + rjumpi(1, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjumpi(-11, 0) + // [1, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::stack_height_mismatch); + + // rjumpi backwards - only min stack height mismatch + add_test_case(eof_bytecode(OP_PUSH0 + OP_PUSH0 + // [2, 2] + rjumpi(1, 0) + // [2, 2] + OP_POP + // [1, 1] + rjumpi(-11, 0) + // [1, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, backwards_rjumpi_variable_stack) +{ + add_test_case(eof_bytecode(varstack + rjumpi(-5, 0) + OP_STOP, 4), EOFValidationError::success); + + add_test_case(eof_bytecode(varstack + OP_PUSH0 + OP_POP + rjumpi(-7, 0) + OP_STOP, 4), + EOFValidationError::success); + + // rjumpi backwards from different locations to the same target + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + OP_POP + rjumpi(-7, 0) + rjumpi(-12, 0) + OP_STOP, 4), + EOFValidationError::success); + + // rjumpi backwards from different locations to the same target - stack height mismatch + add_test_case( + eof_bytecode( + varstack + OP_PUSH0 + OP_POP + rjumpi(-7, 0) + OP_PUSH0 + rjumpi(-13, 0) + OP_STOP, 5), + EOFValidationError::stack_height_mismatch); + + // valid loop + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + push(1) + OP_ADD + rjumpi(-7, OP_DUP1) + OP_STOP, 5), + EOFValidationError::success); + + // pushing loop + add_test_case( + eof_bytecode( + varstack + OP_PUSH0 + push(1) + OP_ADD + OP_DUP1 + rjumpi(-8, OP_DUP1) + OP_STOP, 5), + EOFValidationError::stack_height_mismatch); + + // popping loop + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + OP_PUSH0 + OP_PUSH0 + rjumpi(-4, OP_POP) + OP_STOP, 5), + EOFValidationError::stack_height_mismatch); + + // rjump and rjumpi with the same target - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + OP_POP + // [1, 3] + rjumpi(-7, 0) + // [1, 3] + rjump(-10), // [1, 3] + 4), + EOFValidationError::success); + + // rjump and rjumpi with the same target - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + OP_POP + // [1, 3] + rjumpi(-7, 0) + // [1, 3] + OP_PUSH0 + // [2, 4] + rjump(-11), // [2, 4] + 4), + EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, forwards_rjumpi) +{ + add_test_case(eof_bytecode(rjumpi(0, 1) + OP_STOP, 1), EOFValidationError::success); + + // forwards rjump + fallthrough - equal stack + add_test_case( + eof_bytecode(push0() + rjumpi(1, 0) + OP_NOT + OP_STOP, 2), EOFValidationError::success); + + // forwards rjumpi + forwards rjumpi + fallthrough - equal stack + add_test_case(eof_bytecode(push0() + rjumpi(6, 0) + rjumpi(1, 0) + OP_NOT + OP_STOP, 2), + EOFValidationError::success); + + // forwards rjumpi + fallthrough - different stack + // rjumpi: [1, 1] + // push0: [2, 2] + add_test_case( + eof_bytecode(push0() + rjumpi(1, 0) + OP_PUSH0 + OP_STOP, 2), EOFValidationError::success); + + // forwards rjumpi + forwards rjumpi + fallthrough - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(7, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjumpi(1, 0) + // [2, 2] + OP_NOT + // [2, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::success); + + // valid loop with a break + add_test_case(eof_bytecode(push0() + // [1, 1] + push(1) + // [2, 2] + OP_ADD + // [1, 1] + OP_DUP1 + // [2, 2] + push(10) + // [3, 3] + rjumpi(4, OP_GT) + // [1, 1] + rjumpi(-14, OP_DUP1) + // [1, 1] + OP_STOP, // [1, 1] + 3), + EOFValidationError::success); + + // valid loop with a break - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + push(1) + // [2, 2] + OP_ADD + // [1, 1] + OP_DUP1 + // [2, 2] + push(10) + // [3, 3] + rjumpi(5, OP_GT) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjumpi(-13, OP_DUP1) + // [2, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::success); + + // if-then-else with push - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(4, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjump(1) + // [2, 2] + OP_PUSH0 + // [2, 2] + OP_STOP, // [2, 2] + 2), + EOFValidationError::success); + + // if-then-else with push - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(4, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjump(1) + // [2, 2] + OP_NOT + // [1, 1] + OP_STOP, // [1, 2] + 2), + EOFValidationError::success); + + // if-then-else with pop - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(4, 0) + // [1, 1] + OP_POP + // [0, 0] + rjump(1) + // [0, 0] + OP_POP + // [0, 0] + OP_STOP, // [0, 0] + 2), + EOFValidationError::success); + + // if-then-else with pop - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(4, 0) + // [1, 1] + OP_POP + // [0, 0] + rjump(1) + // [0, 0] + OP_NOT + // [1, 1] + OP_STOP, // [0, 1] + 2), + EOFValidationError::success); + + // rjump and rjumpi with the same target - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(3, 0) + // [1, 1] + rjump(0) + // [1, 1] + OP_STOP, // [1, 1] + 2), + EOFValidationError::success); + + // rjump and rjumpi with the same target - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(4, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjump(0) + // [2, 2] + OP_STOP, // [1, 2] + 2), + EOFValidationError::success); +} + +TEST_F(eof_validation, forwards_rjumpi_variable_stack) +{ + add_test_case(eof_bytecode(varstack + rjumpi(0, 1) + OP_STOP, 4), EOFValidationError::success); + + // forwards rjump + fallthrough - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + rjumpi(1, 0) + OP_NOT + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjumpi + forwards rjumpi + fallthrough - equal stack + add_test_case( + eof_bytecode(varstack + OP_PUSH0 + rjumpi(6, 0) + rjumpi(1, 0) + OP_NOT + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjumpi + fallthrough - different stack + // rjumpi: [4, 4] + // push0: [5, 5] + add_test_case(eof_bytecode(varstack + OP_PUSH0 + rjumpi(1, 0) + OP_PUSH0 + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjumpi + forwards rjumpi + fallthrough - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(7, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjumpi(1, 0) + // [3, 5] + OP_NOT + // [3, 5] + OP_STOP, // [2, 5] + 6), + EOFValidationError::success); + + // valid loop with a break + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + push(1) + // [3, 5] + OP_ADD + // [2, 4] + OP_DUP1 + // [3, 5] + push(10) + // [4, 6] + rjumpi(4, OP_GT) + // [2, 4] + rjumpi(-14, OP_DUP1) + // [2, 4] + OP_STOP, // [2, 4] + 6), + EOFValidationError::success); + + // valid loop with a break - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + push(1) + // [3, 5] + OP_ADD + // [2, 4] + OP_DUP1 + // [3, 5] + push(10) + // [4, 6] + rjumpi(5, OP_GT) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjumpi(-13, OP_DUP1) + // [3, 5] + OP_STOP, // [2, 5] + 6), + EOFValidationError::success); + + // if-then-else with push - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(4, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjump(1) + // [3, 5] + OP_PUSH0 + // [3, 5] + OP_STOP, // [3, 5] + 5), + EOFValidationError::success); + + // if-then-else with push - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(4, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjump(1) + // [3, 5] + OP_NOT + // [2, 4] + OP_STOP, // [2, 5] + 5), + EOFValidationError::success); + + // if-then-else with pop - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(4, 0) + // [2, 4] + OP_POP + // [1, 3] + rjump(1) + // [1, 3] + OP_POP + // [1, 3] + OP_STOP, // [1, 3] + 5), + EOFValidationError::success); + + // if-then-else with pop - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(4, 0) + // [2, 4] + OP_POP + // [1, 3] + rjump(1) + // [1, 3] + OP_NOT + // [2, 4] + OP_STOP, // [1, 4] + 5), + EOFValidationError::success); + + // rjump and rjumpi with the same target - equal stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(3, 0) + // [2, 4] + rjump(0) + // [2, 4] + OP_STOP, // [2, 4] + 5), + EOFValidationError::success); + + // rjump and rjumpi with the same target - different stack + add_test_case(eof_bytecode(varstack + OP_PUSH0 + // [2, 4] + rjumpi(4, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjump(0) + // [3, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::success); +} + +TEST_F(eof_validation, backwards_rjumpv) +{ + add_test_case(eof_bytecode(rjumpv({-6}, 0) + OP_STOP, 1), EOFValidationError::success); + + add_test_case( + eof_bytecode(push0() + OP_POP + rjumpv({-8}, 0) + OP_STOP, 1), EOFValidationError::success); + + // rjumpv backwards from different locations to the same target + add_test_case(eof_bytecode(push0() + OP_POP + rjumpv({-8}, 0) + rjumpv({-14}, 0) + OP_STOP, 1), + EOFValidationError::success); + + // rjumpv backwards from different locations to the same target - stack height mismatch + add_test_case( + eof_bytecode(push0() + OP_POP + rjumpv({-8}, 0) + push0() + rjumpv({-15}, 0) + OP_STOP, 2), + EOFValidationError::stack_height_mismatch); + + // rjump and rjumpv with the same target - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + OP_POP + // [0, 0] + rjumpv({-8}, 0) + // [0, 0] + rjump(-11), // [0, 0] + 1), + EOFValidationError::success); + + // rjump and rjumpv with the same target - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + OP_POP + // [0, 0] + rjumpv({-8}, 0) + // [0, 0] + OP_PUSH0 + // [1, 1] + rjump(-12), // [1, 1] + 1), + EOFValidationError::stack_height_mismatch); + + // rjumpv backwards - only max stack height mismatch + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpi(1, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjumpv({-12}, 0) + // [1, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::stack_height_mismatch); + + // rjumpv backwards - only min stack height mismatch + add_test_case(eof_bytecode(OP_PUSH0 + OP_PUSH0 + // [2, 2] + rjumpi(1, 0) + // [2, 2] + OP_POP + // [1, 1] + rjumpv({-12}, 0) + // [1, 2] + OP_STOP, // [1, 2] + 3), + EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, backwards_rjumpv_variable_stack) +{ + add_test_case( + eof_bytecode(varstack + rjumpv({-6}, 0) + OP_STOP, 4), EOFValidationError::success); + + add_test_case(eof_bytecode(varstack + push0() + OP_POP + rjumpv({-8}, 0) + OP_STOP, 4), + EOFValidationError::success); + + // rjumpv backwards from different locations to the same target + add_test_case( + eof_bytecode(varstack + push0() + OP_POP + rjumpv({-8}, 0) + rjumpv({-14}, 0) + OP_STOP, 4), + EOFValidationError::success); + + // rjumpv backwards from different locations to the same target - stack height mismatch + add_test_case(eof_bytecode(varstack + push0() + OP_POP + rjumpv({-8}, 0) + push0() + + rjumpv({-15}, 0) + OP_STOP, + 5), + EOFValidationError::stack_height_mismatch); + + // rjump and rjumpv with the same target - equal stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + OP_POP + // [1, 3] + rjumpv({-8}, 0) + // [1, 3] + rjump(-11), // [1, 3] + 4), + EOFValidationError::success); + + // rjump and rjumpv with the same target - different stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + OP_POP + // [1, 3] + rjumpv({-8}, 0) + // [1, 3] + OP_PUSH0 + // [2, 4] + rjump(-12), // [2, 4] + 4), + EOFValidationError::stack_height_mismatch); + + // rjumpv backwards - only max stack height mismatch + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + rjumpi(1, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjumpv({-12}, 0) + // [2, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::stack_height_mismatch); + + // rjumpv backwards - only min stack height mismatch + add_test_case(eof_bytecode(varstack + OP_PUSH0 + OP_PUSH0 + // [3, 5] + rjumpi(1, 0) + // [3, 5] + OP_POP + // [2, 4] + rjumpv({-12}, 0) + // [2, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::stack_height_mismatch); +} + +TEST_F(eof_validation, forwards_rjumpv) +{ + add_test_case(eof_bytecode(rjumpv({0}, 1) + OP_STOP, 1), EOFValidationError::success); + + // forwards rjumpv + fallthrough - equal stack + add_test_case( + eof_bytecode(push0() + rjumpv({1}, 0) + OP_NOT + OP_STOP, 2), EOFValidationError::success); + + // forwards rjumpv 2 cases + fallthrough - equal stack + add_test_case( + eof_bytecode(push0() + rjumpv({2, 3}, 0) + OP_PUSH0 + OP_POP + OP_NOT + OP_STOP, 2), + EOFValidationError::success); + + // forwards rjumpv + fallthrough - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpv({1}, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + OP_STOP, // [1, 2] + 2), + EOFValidationError::success); + + // forwards rjumpv 2 cases + fallthrough - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpv({1, 2}, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + OP_PUSH0 + // [2, 3] + OP_NOT + // [1, 3] + OP_STOP, // [1, 2] + 3), + EOFValidationError::success); + + // switch - equal stack + add_test_case(eof_bytecode(push0() + rjumpv({5, 10}, 0) + // [1, 1] + push(1) + rjump(7) + // [2, 2] + push(2) + rjump(2) + // [2, 2] + push(3) + // [2, 2] + OP_STOP, // [2, 2] + 2), + EOFValidationError::success); + + // switch with pushes - different stack + add_test_case(eof_bytecode(push0() + rjumpv({4, 9}, 0) + // [1, 1] + push0() + rjump(8) + // [2, 2] + push0() + push0() + rjump(3) + // [3, 3] + push0() + push0() + push0() + // [4, 4] + OP_STOP, // [1, 4] + 4), + EOFValidationError::success); + + + // switch with pops - different stack + add_test_case(eof_bytecode(4 * push0() + rjumpv({4, 9}, 0) + // [4, 4] + OP_POP + rjump(8) + // [3, 3] + OP_POP + OP_POP + rjump(3) + // [2, 2] + OP_POP + OP_POP + OP_POP + // [1, 1] + OP_STOP, // [1, 4] + 5), + EOFValidationError::success); + + // rjump and rjumpv with the same target - equal stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpv({3}, 0) + // [1, 1] + rjump(0) + // [1, 1] + OP_STOP, // [1, 1] + 2), + EOFValidationError::success); + + // rjump and rjumpv with the same target - different stack + add_test_case(eof_bytecode(push0() + // [1, 1] + rjumpv({4}, 0) + // [1, 1] + OP_PUSH0 + // [2, 2] + rjump(0) + // [2, 2] + OP_STOP, // [1, 2] + 2), + EOFValidationError::success); +} + +TEST_F(eof_validation, forwards_rjumpv_variable_stack) +{ + add_test_case( + eof_bytecode(varstack + rjumpv({0}, 1) + OP_STOP, 4), EOFValidationError::success); + + // forwards rjumpv + fallthrough - equal stack + add_test_case(eof_bytecode(varstack + push0() + rjumpv({1}, 0) + OP_NOT + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjumpv 2 cases + fallthrough - equal stack + add_test_case( + eof_bytecode( + varstack + push0() + rjumpv({2, 3}, 0) + OP_PUSH0 + OP_POP + OP_NOT + OP_STOP, 5), + EOFValidationError::success); + + // forwards rjumpv + fallthrough - different stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + rjumpv({1}, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::success); + + // forwards rjumpv 2 cases + fallthrough - different stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + rjumpv({1, 2}, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + OP_PUSH0 + // [2, 6] + OP_NOT + // [2, 6] + OP_STOP, // [2, 6] + 6), + EOFValidationError::success); + + // switch - equal stack + add_test_case(eof_bytecode(varstack + push0() + rjumpv({5, 10}, 0) + // [2, 4] + push(1) + rjump(7) + // [3, 5] + push(2) + rjump(2) + // [3, 5] + push(3) + // [3, 5] + OP_STOP, // [3, 5] + 5), + EOFValidationError::success); + + // switch with pushes - different stack + add_test_case(eof_bytecode(varstack + push0() + rjumpv({4, 9}, 0) + // [2, 4] + push0() + rjump(8) + // [3, 5] + push0() + push0() + rjump(3) + // [4, 6] + push0() + push0() + push0() + // [5, 7] + OP_STOP, // [3, 7] + 7), + EOFValidationError::success); + + + // switch with pops - different stack + add_test_case(eof_bytecode(varstack + 4 * push0() + rjumpv({4, 9}, 0) + // [5, 7] + OP_POP + rjump(8) + // [4, 6] + OP_POP + OP_POP + rjump(3) + // [3, 5] + OP_POP + OP_POP + OP_POP + // [2, 4] + OP_STOP, // [2, 6] + 8), + EOFValidationError::success); + + // rjump and rjumpv with the same target - equal stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + rjumpv({3}, 0) + // [2, 4] + rjump(0) + // [2, 4] + OP_STOP, // [2, 4] + 5), + EOFValidationError::success); + + // rjump and rjumpv with the same target - different stack + add_test_case(eof_bytecode(varstack + push0() + // [2, 4] + rjumpv({4}, 0) + // [2, 4] + OP_PUSH0 + // [3, 5] + rjump(0) + // [3, 5] + OP_STOP, // [2, 5] + 5), + EOFValidationError::success); +} + +TEST_F(eof_validation, self_referencing_jumps) +{ + // rjumpf from stack 0 to stack 0 + add_test_case(eof_bytecode(rjump(-3)), EOFValidationError::success, "rjump"); + + // rjumpi from stack 0 to stack 1 + add_test_case( + eof_bytecode(rjumpi(-3, 0) + OP_STOP), EOFValidationError::stack_height_mismatch, "rjumpi"); + + // rjumpv from stack 0 to stack 1 + add_test_case(eof_bytecode(rjumpv({-4}, 0) + OP_STOP), + EOFValidationError::stack_height_mismatch, "rjumpv"); +} + +TEST_F(eof_validation, self_referencing_jumps_variable_stack) +{ + // rjumpf from stack [1, 3] to stack [1, 3] + add_test_case(eof_bytecode(varstack + rjump(-3), 3), EOFValidationError::success, "rjump"); + + // rjumpi from stack [1, 3] to stack [2, 4] + add_test_case(eof_bytecode(varstack + rjumpi(-3, 0) + OP_STOP, 4), + EOFValidationError::stack_height_mismatch, "rjumpi"); + + // rjumpv from stack [1, 3] to stack [2, 4] + add_test_case(eof_bytecode(varstack + rjumpv({-4}, 0) + OP_STOP, 4), + EOFValidationError::stack_height_mismatch, "rjumpv"); +} + +TEST_F(eof_validation, underflow) +{ + add_test_case(eof_bytecode(bytecode{OP_ADD} + OP_STOP, 0), EOFValidationError::stack_underflow); + + // CALLF underflow + add_test_case(eof_bytecode(callf(1) + OP_STOP, 1).code(push0() + OP_RETF, 1, 2, 2), + EOFValidationError::stack_underflow); + + // JUMPF to returning function + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(jumpf(2), 0, 2, 0) + .code(push0() + OP_RETF, 1, 2, 2), + EOFValidationError::stack_underflow); + + // JUMPF to non-returning function + add_test_case(eof_bytecode(jumpf(1), 0).code(revert(0, 0), 1, 0x80, 3), + EOFValidationError::stack_underflow); + + // RETF underflow + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2).code(push0() + OP_RETF, 0, 2, 1), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, underflow_variable_stack) +{ + // LOG2 underflow - [1, 3] stack - neither min nor max enough for 4 inputs + add_test_case( + eof_bytecode(varstack + OP_LOG2 + OP_STOP, 3), EOFValidationError::stack_underflow); + + // ADD underflow - [1, 3] stack - only max enough for 5 inputs + add_test_case( + eof_bytecode(varstack + OP_ADD + OP_STOP, 3), EOFValidationError::stack_underflow); + + // CALLF underflow - [1, 3] stack - neither min nor max enough for 5 inputs + add_test_case(eof_bytecode(varstack + callf(1) + OP_STOP, 4).code(push0() + OP_RETF, 4, 5, 5), + EOFValidationError::stack_underflow); + + // CALLF underflow - [1, 3] stack - only max enough for 3 inputs + add_test_case(eof_bytecode(varstack + callf(1) + OP_STOP, 4).code(push0() + OP_RETF, 3, 4, 4), + EOFValidationError::stack_underflow); + + // JUMPF to returning function - neither min nor max enough for 5 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(bytecode{OP_POP} + OP_POP + OP_RETF, 5, 3, 3), + EOFValidationError::stack_underflow); + + // JUMPF to returning function - only max enough for 3 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(bytecode{OP_RETF}, 3, 3, 3), + EOFValidationError::stack_underflow); + + // JUMPF to non-returning function - [1, 3] stack - neither min nor max enough for 5 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 0).code(revert(0, 0), 5, 0x80, 7), + EOFValidationError::stack_underflow); + + // JUMPF to non-returning function - [1, 3] stack - only max enough for 3 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 0).code(revert(0, 0), 3, 0x80, 5), + EOFValidationError::stack_underflow); + + // RETF from [1, 3] stack - neither min nor max enough for 5 outputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 5).code(varstack + OP_RETF, 0, 5, 3), + EOFValidationError::stack_underflow); + + // RETF from [1, 3] stack - only max enough for 3 outputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3).code(varstack + OP_RETF, 0, 3, 3), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, callf_stack_validation) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP, 1) + .code(push0() + push0() + callf(2) + OP_RETF, 0, 1, 2) + .code(bytecode(OP_POP) + OP_RETF, 2, 1, 2), + EOFValidationError::success); + + add_test_case(eof_bytecode(callf(1) + OP_STOP, 1) + .code(push0() + push0() + push0() + callf(2) + OP_RETF, 0, 1, 3) + .code(bytecode(OP_POP) + OP_RETF, 2, 1, 2), + EOFValidationError::stack_higher_than_outputs_required); + + add_test_case(eof_bytecode(callf(1) + OP_STOP, 1) + .code(push0() + callf(2) + OP_RETF, 0, 1, 1) + .code(bytecode(OP_POP) + OP_RETF, 2, 1, 2), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, callf_stack_overflow) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP) + .code(512 * push(1) + callf(1) + 512 * OP_POP + OP_RETF, 0, 0, 512), + EOFValidationError::success); + + add_test_case(eof_bytecode(callf(1) + OP_STOP) + .code(513 * push(1) + callf(1) + 513 * OP_POP + OP_RETF, 0, 0, 513), + EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(callf(1) + OP_STOP) + .code(1023 * push(1) + callf(1) + 1023 * OP_POP + OP_RETF, 0, 0, 1023), + EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(callf(1) + OP_STOP) + .code(1023 * push(1) + callf(2) + 1023 * OP_POP + OP_RETF, 0, 0, 1023) + .code(push0() + OP_POP + OP_RETF, 0, 0, 1), + EOFValidationError::success); + + add_test_case(eof_bytecode(callf(1) + OP_STOP) + .code(1023 * push(1) + callf(2) + 1023 * OP_POP + OP_RETF, 0, 0, 1023) + .code(push0() + push0() + OP_POP + OP_POP + OP_RETF, 0, 0, 2), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, callf_stack_overflow_variable_stack) +{ + add_test_case(eof_bytecode(varstack + 509 * push(1) + callf(1) + OP_STOP, 512) + .code(512 * push(1) + 512 * OP_POP + OP_RETF, 0, 0, 512), + EOFValidationError::success); + + // CALLF from [510, 512] stack to function with 515 max stack - both min and max stack overflow + add_test_case(eof_bytecode(varstack + 509 * push(1) + callf(1) + OP_STOP, 512) + .code(515 * push(1) + 515 * OP_POP + OP_RETF, 0, 0, 515), + EOFValidationError::stack_overflow); + + // CALLF from [510, 512] stack to function with 514 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 509 * push(1) + callf(1) + OP_STOP, 512) + .code(514 * push(1) + 514 * OP_POP + OP_RETF, 0, 0, 514), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to function with 1 max stack + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + OP_POP + OP_RETF, 0, 0, 1), + EOFValidationError::success); + + // CALLF from [1021, 1023] stack to function with 5 max stack - both min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(5 * push0() + 5 * OP_POP + OP_RETF, 0, 0, 5), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to function with 2 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + push0() + OP_POP + OP_POP + OP_RETF, 0, 0, 2), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, callf_with_inputs_stack_overflow) +{ + add_test_case(eof_bytecode(1023 * push(1) + callf(1) + 1019 * OP_POP + OP_RETURN, 1023) + .code(bytecode{OP_POP} + OP_POP + OP_RETF, 2, 0, 2), + EOFValidationError::success); + + add_test_case(eof_bytecode(1023 * push(1) + callf(1) + 1021 * OP_POP + OP_RETURN, 1023) + .code(push(1) + OP_POP + OP_RETF, 3, 3, 4), + EOFValidationError::success); + + add_test_case(eof_bytecode(1023 * push(1) + callf(1) + 1021 * OP_POP + OP_RETURN, 1023) + .code(push0() + push0() + OP_RETF, 3, 5, 5), + EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(1023 * push(1) + callf(1) + 1021 * OP_POP + OP_RETURN, 1023) + .code(push0() + push0() + OP_POP + OP_POP + OP_RETF, 3, 3, 5), + EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(1024 * push(1) + callf(1) + 1020 * OP_POP + OP_RETURN, 1023) + .code(push0() + OP_POP + OP_POP + OP_POP + OP_RETF, 2, 0, 3), + EOFValidationError::stack_overflow); + + add_test_case( + eof_bytecode(1023 * push(1) + callf(1) + 1020 * OP_POP + OP_RETURN, 1023) + .code(push0() + push0() + OP_POP + OP_POP + OP_POP + OP_POP + OP_RETF, 2, 0, 4), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, callf_with_inputs_stack_overflow_variable_stack) +{ + // CALLF from [1021, 1023] stack to function with 2 inputs and 2 max stack + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(bytecode{OP_POP} + OP_POP + OP_RETF, 2, 0, 2), + EOFValidationError::success); + + // CALLF from [1021, 1023] stack to function with 3 inputs and 4 max stack + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push(1) + OP_POP + OP_RETF, 3, 3, 4), + EOFValidationError::success); + + // CALLF from [1021, 1023] stack to 3 inputs and 7 outputs - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(4 * push0() + OP_RETF, 3, 7, 7), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to 3 inputs and 5 outputs - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + push0() + OP_RETF, 3, 5, 5), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to 3 inputs and 7 max stack - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(4 * push0() + OP_POP + OP_POP + OP_RETF, 3, 3, 7), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to 3 inputs and 5 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + push0() + OP_POP + OP_POP + OP_RETF, 3, 3, 5), + EOFValidationError::stack_overflow); + + // CALLF from [1022, 1024] stack to 2 inputs and 5 max stack - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1021 * push(1) + callf(1) + OP_STOP, 1023) + .code(3 * push0() + 5 * OP_POP + OP_RETF, 2, 0, 5), + EOFValidationError::stack_overflow); + + // CALLF from [1022, 1024] stack to 2 inputs and 3 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1021 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + OP_POP + OP_POP + OP_POP + OP_RETF, 2, 0, 3), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to 2 inputs and 6 max stack - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(4 * push0() + 6 * OP_POP + OP_RETF, 2, 0, 6), + EOFValidationError::stack_overflow); + + // CALLF from [1021, 1023] stack to 2 inputs and 4 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push(1) + callf(1) + OP_STOP, 1023) + .code(push0() + push0() + 4 * OP_POP + OP_RETF, 2, 0, 4), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, retf_stack_validation) +{ + // 2 outputs, RETF has 2 values on stack + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2).code(push0() + push0() + OP_RETF, 0, 2, 2), + EOFValidationError::success); + + // 2 outputs, RETF has 1 value on stack + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2).code(push0() + OP_RETF, 0, 2, 1), + EOFValidationError::stack_underflow); + + // 2 outputs, RETF has 3 values on stack + add_test_case( + eof_bytecode(callf(1) + OP_STOP, 2).code(push0() + push0() + push0() + OP_RETF, 0, 2, 3), + EOFValidationError::stack_higher_than_outputs_required); + + // RETF in a helper (reached via different paths) + add_test_case( + eof_bytecode(push0() + callf(1) + OP_STOP, 2) + .code(rjumpi(7, {}) + push(1) + push(1) + rjump(2) + push0() + push0() + OP_RETF, 1, 2, + 2), + EOFValidationError::success); +} + +TEST_F(eof_validation, retf_variable_stack) +{ + // RETF in variable stack segment is not allowed and always fails with one of two errors + + // RETF from [1, 3] stack returning 5 outputs + auto code = eof_bytecode(callf(1) + OP_STOP, 5).code(varstack + OP_RETF, 0, 5, 3); + add_test_case(code, EOFValidationError::stack_underflow); + + // RETF from [1, 3] stack returning 3 outputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3).code(varstack + OP_RETF, 0, 3, 3), + EOFValidationError::stack_underflow); + + // RETF from [1, 3] stack returning 1 output + add_test_case(eof_bytecode(callf(1) + OP_STOP, 1).code(varstack + OP_RETF, 0, 1, 3), + EOFValidationError::stack_higher_than_outputs_required); + + // RETF from [1, 3] returning 0 outputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 0).code(varstack + OP_RETF, 0, 0, 3), + EOFValidationError::stack_higher_than_outputs_required); +} + +TEST_F(eof_validation, jumpf_to_returning) +{ + // JUMPF into a function with the same number of outputs as current one + + // 0 inputs target + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(jumpf(2), 0, 2, 0) + .code(push0() + push0() + OP_RETF, 0, 2, 2), + EOFValidationError::success); + + // 0 inputs target - extra items on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + jumpf(2), 0, 2, 2) + .code(push0() + push0() + OP_RETF, 0, 2, 2), + EOFValidationError::stack_higher_than_outputs_required); + + // Exactly required inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + jumpf(2), 0, 2, 3) + .code(bytecode(OP_POP) + OP_RETF, 3, 2, 3), + EOFValidationError::success); + + // Extra items on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + push0() + jumpf(2), 0, 2, 4) + .code(bytecode(OP_POP) + OP_RETF, 3, 2, 3), + EOFValidationError::stack_higher_than_outputs_required); + + // Not enough inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + jumpf(2), 0, 2, 2) + .code(bytecode(OP_POP) + OP_RETF, 3, 2, 3), + EOFValidationError::stack_underflow); + + // JUMPF into a function with fewer outputs than current one + + // (0, 2) --JUMPF--> (0, 1): 0 inputs + 1 output = 1 item required + + // Exactly required inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + jumpf(2), 0, 2, 1) + .code(push0() + OP_RETF, 0, 1, 1), + EOFValidationError::success); + + // Extra items on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + jumpf(2), 0, 2, 3) + .code(push0() + OP_RETF, 0, 1, 1), + EOFValidationError::stack_higher_than_outputs_required); + + // Not enough inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(jumpf(2), 0, 2, 0) + .code(push0() + OP_RETF, 0, 1, 1), + EOFValidationError::stack_underflow); + + // (0, 2) --JUMPF--> (3, 1): 3 inputs + 1 output = 4 items required + + // Exactly required inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + push0() + jumpf(2), 0, 2, 4) + .code(bytecode(OP_POP) + OP_POP + OP_RETF, 3, 1, 3), + EOFValidationError::success); + + // Extra items on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + push0() + push0() + jumpf(2), 0, 2, 5) + .code(bytecode(OP_POP) + OP_POP + OP_RETF, 3, 1, 3), + EOFValidationError::stack_higher_than_outputs_required); + + // Not enough inputs on stack at JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(push0() + push0() + push0() + jumpf(2), 0, 2, 3) + .code(bytecode(OP_POP) + OP_POP + OP_RETF, 3, 1, 3), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, jumpf_to_returning_variable_stack) +{ + // JUMPF to returning function in variable stack segment is not allowed and always fails with + // one of two errors + + // JUMPF into a function with the same number of outputs as current one + + // JUMPF from [1, 3] stack to function with 5 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(push0() + OP_RETF, 5, 3, 3), + EOFValidationError::stack_underflow); + + // JUMPF from [1, 3] stack to function with 3 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(bytecode{OP_RETF}, 3, 3, 3), + EOFValidationError::stack_underflow); + + // JUMPF from [1, 3] stack to function with 1 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(push0() + push0() + OP_RETF, 1, 3, 5), + EOFValidationError::stack_higher_than_outputs_required); + + // JUMPF from [1, 3] stack to function with 0 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(varstack + jumpf(2), 0, 3, 3) + .code(push0() + push0() + push0() + OP_RETF, 0, 3, 3), + EOFValidationError::stack_higher_than_outputs_required); + + // JUMPF into a function with fewer outputs than current one + + // JUMPF from [1, 3] stack to function with 5 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(varstack + jumpf(2), 0, 2, 3) + .code(4 * OP_POP + OP_RETF, 5, 1, 5), + EOFValidationError::stack_underflow); + + // JUMPF from [1, 3] stack to function with 3 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(varstack + jumpf(2), 0, 2, 3) + .code(bytecode{OP_POP} + OP_POP + bytecode{OP_RETF}, 3, 1, 3), + EOFValidationError::stack_underflow); + + // JUMPF from [1, 3] stack to function with 1 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(varstack + jumpf(2), 0, 2, 3) + .code(OP_RETF, 1, 1, 1), + EOFValidationError::stack_higher_than_outputs_required); + + // JUMPF from [1, 3] stack to function with 0 inputs + add_test_case(eof_bytecode(callf(1) + OP_STOP, 2) + .code(varstack + jumpf(2), 0, 2, 3) + .code(push0() + OP_RETF, 0, 1, 1), + EOFValidationError::stack_higher_than_outputs_required); +} + +TEST_F(eof_validation, jumpf_to_nonreturning) +{ + // Target has 0 inputs + + // 0 inputs on stack at JUMPF + add_test_case(eof_bytecode(jumpf(1)).code(OP_STOP, 0, 0x80, 0), EOFValidationError::success); + + // Extra items on stack at JUMPF + add_test_case(eof_bytecode(push0() + push0() + jumpf(1), 2).code(OP_STOP, 0, 0x80, 0), + EOFValidationError::success); + + // Target has 3 inputs + + // Exactly required inputs on stack at JUMPF + add_test_case(eof_bytecode(3 * OP_PUSH0 + jumpf(1), 3).code(OP_STOP, 3, 0x80, 3), + EOFValidationError::success); + + // Extra items on stack at JUMPF + add_test_case(eof_bytecode(4 * OP_PUSH0 + jumpf(1), 4).code(OP_STOP, 3, 0x80, 3), + EOFValidationError::success); + + // Not enough inputs on stack at JUMPF + add_test_case(eof_bytecode(2 * OP_PUSH0 + jumpf(1), 2).code(OP_STOP, 3, 0x80, 3), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, jumpf_to_nonreturning_variable_stack) +{ + // JUMPF from [1, 3] stack to non-returning function with 5 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 5, 0x80, 5), + EOFValidationError::stack_underflow); + + // JUMPF from [1, 3] stack to non-returning function with 3 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 3, 0x80, 3), + EOFValidationError::stack_underflow); + + // Extra items on stack are allowed for JUMPF to non-returning + + // JUMPF from [1, 3] stack to non-returning function with 1 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 1, 0x80, 1), + EOFValidationError::success); + + // JUMPF from [1, 3] stack to non-returning function with 0 inputs + add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 0, 0x80, 0), + EOFValidationError::success); +} + +TEST_F(eof_validation, jumpf_stack_overflow) +{ + add_test_case(eof_bytecode(512 * push(1) + jumpf(0), 512), EOFValidationError::success); + + add_test_case(eof_bytecode(513 * push(1) + jumpf(0), 513), EOFValidationError::stack_overflow); + + add_test_case( + eof_bytecode(1023 * push(1) + jumpf(0), 1023), EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(1023 * push(1) + jumpf(1), 1023).code(push0() + OP_STOP, 0, 0x80, 1), + EOFValidationError::success); + + add_test_case( + eof_bytecode(1023 * push(1) + jumpf(1), 1023).code(push0() + push0() + OP_STOP, 0, 0x80, 2), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, jumpf_stack_overflow_variable_stack) +{ + add_test_case( + eof_bytecode(varstack + 509 * OP_PUSH0 + jumpf(0), 512), EOFValidationError::success); + + // JUMPF from [510, 512] stack to function with 515 max stack - both min and max stack overflow + add_test_case(eof_bytecode(varstack + 509 * OP_PUSH0 + jumpf(1), 512) + .code(515 * OP_PUSH0 + OP_STOP, 0, 0x80, 515), + EOFValidationError::stack_overflow); + + // JUMPF from [510, 512] stack to function with 514 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 509 * OP_PUSH0 + jumpf(1), 512) + .code(514 * OP_PUSH0 + OP_STOP, 0, 0x80, 514), + EOFValidationError::stack_overflow); + + // JUMPF from [1021, 1023] stack to function with 1 max stack + add_test_case(eof_bytecode(varstack + 1020 * OP_PUSH0 + jumpf(1), 1023) + .code(push0() + OP_STOP, 0, 0x80, 1), + EOFValidationError::success); + + // JUMPF from [1021, 1023] stack to function with 5 max stack - both min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * OP_PUSH0 + jumpf(1), 1023) + .code(5 * push0() + OP_STOP, 0, 0x80, 5), + EOFValidationError::stack_overflow); + + // JUMPF from [1021, 1023] stack to function with 2 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * OP_PUSH0 + jumpf(1), 1023) + .code(push0() + push0() + OP_STOP, 0, 0x80, 2), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, jumpf_with_inputs_stack_overflow) +{ + add_test_case(eof_bytecode(1023 * push0() + jumpf(1), 1023).code(push0() + OP_STOP, 2, 0x80, 3), + EOFValidationError::success); + + add_test_case( + eof_bytecode(1023 * push0() + jumpf(1), 1023).code(push0() + push0() + OP_STOP, 2, 0x80, 4), + EOFValidationError::stack_overflow); + + add_test_case(eof_bytecode(1024 * push0() + jumpf(1), 1023).code(push0() + OP_STOP, 2, 0x80, 3), + EOFValidationError::stack_overflow); +} + + +TEST_F(eof_validation, jumpf_with_inputs_stack_overflow_variable_stack) +{ + // JUMPF from [1021, 1023] stack to 2 inputs and 3 max stack + add_test_case(eof_bytecode(varstack + 1020 * push0() + jumpf(1), 1023) + .code(push0() + OP_STOP, 2, 0x80, 3), + EOFValidationError::success); + + // JUMPF from [1021, 1023] stack to 2 inputs and 6 max stack - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push0() + jumpf(1), 1023) + .code(4 * push0() + OP_STOP, 2, 0x80, 6), + EOFValidationError::stack_overflow); + + // JUMPF from [1021, 1023] stack to 2 inputs and 4 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1020 * push0() + jumpf(1), 1023) + .code(push0() + push0() + OP_STOP, 2, 0x80, 4), + EOFValidationError::stack_overflow); + + // JUMPF from [1022, 1024] stack to 2 inputs and 5 max stack - min and max stack overflow + add_test_case(eof_bytecode(varstack + 1021 * push0() + jumpf(1), 1023) + .code(3 * push0() + OP_STOP, 2, 0x80, 5), + EOFValidationError::stack_overflow); + + // JUMPF from [1022, 1024] stack to 2 inputs and 3 max stack - only max stack overflow + add_test_case(eof_bytecode(varstack + 1021 * push0() + jumpf(1), 1023) + .code(push0() + OP_STOP, 2, 0x80, 3), + EOFValidationError::stack_overflow); +} + +TEST_F(eof_validation, dupn_stack_validation) +{ + const auto pushes = 20 * push(1); + add_test_case(eof_bytecode(pushes + OP_DUPN + "00" + OP_STOP, 21), EOFValidationError::success); + add_test_case(eof_bytecode(pushes + OP_DUPN + "13" + OP_STOP, 21), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_DUPN + "14" + OP_STOP, 21), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_DUPN + "d0" + OP_STOP, 21), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_DUPN + "fe" + OP_STOP, 21), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_DUPN + "ff" + OP_STOP, 21), EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, swapn_stack_validation) +{ + const auto pushes = 20 * push(1); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "00" + OP_STOP, 20), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "12" + OP_STOP, 20), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "13" + OP_STOP, 20), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "d0" + OP_STOP, 20), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "fe" + OP_STOP, 20), EOFValidationError::stack_underflow); + add_test_case( + eof_bytecode(pushes + OP_SWAPN + "ff" + OP_STOP, 20), EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, exchange_stack_validation) +{ + const auto pushes = 10 * push(1); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "00" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "10" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "01" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "20" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "02" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "70" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "07" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "11" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "34" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "43" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "16" + OP_STOP, 10), EOFValidationError::success); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "61" + OP_STOP, 10), EOFValidationError::success); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "80" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "08" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "71" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "17" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "44" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "53" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "35" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "ee" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "ef" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "fe" + OP_STOP, 10), + EOFValidationError::stack_underflow); + add_test_case(eof_bytecode(pushes + OP_EXCHANGE + "ff" + OP_STOP, 10), + EOFValidationError::stack_underflow); +} + +TEST_F(eof_validation, exchange_deep_stack_validation) +{ + const auto pushes = 33 * push(1); + add_test_case( + eof_bytecode(pushes + OP_EXCHANGE + "ff" + OP_STOP, 33), EOFValidationError::success); +} + +TEST_F(eof_validation, exchange_empty_stack_validation) +{ + add_test_case(eof_bytecode(bytecode(OP_EXCHANGE) + "00" + OP_STOP, 0), + EOFValidationError::stack_underflow); +} diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index 39a5dcf4..82349108 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -2,6 +2,8 @@ // Copyright 2021 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include "eof_validation.hpp" +#include #include #include #include @@ -9,222 +11,209 @@ #include using namespace evmone; +using namespace evmone::test; -namespace +TEST_F(eof_validation, before_activation) { -// Can be called as validate_eof(string_view hex, rev) or validate_eof(bytes_view cont, rev). -inline EOFValidationError validate_eof( - const bytecode& container, evmc_revision rev = EVMC_CANCUN) noexcept -{ - return evmone::validate_eof(rev, container); -} -} // namespace - -TEST(eof_validation, get_error_message) -{ - EXPECT_EQ(evmone::get_error_message(EOFValidationError::success), "success"); - EXPECT_EQ(evmone::get_error_message(EOFValidationError::invalid_prefix), "invalid_prefix"); - EXPECT_EQ(evmone::get_error_message(EOFValidationError::impossible), "impossible"); - EXPECT_EQ(evmone::get_error_message(static_cast(-1)), ""); + ASSERT_EQ( + evmone::validate_eof(EVMC_CANCUN, ContainerKind::runtime, bytes(eof_bytecode(OP_STOP))), + EOFValidationError::eof_version_unknown); } -TEST(eof_validation, validate_empty_code) +TEST_F(eof_validation, validate_empty_code) { - EXPECT_EQ(validate_eof(""), EOFValidationError::invalid_prefix); + add_test_case("", EOFValidationError::invalid_prefix); } -TEST(eof_validation, validate_EOF_prefix) +TEST_F(eof_validation, validate_EOF_prefix) { - EXPECT_EQ(validate_eof("00"), EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("FE"), EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("EF"), EOFValidationError::invalid_prefix); + add_test_case("00", EOFValidationError::invalid_prefix); + add_test_case("FE", EOFValidationError::invalid_prefix); + add_test_case("EF", EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("EF0101"), EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("EFEF01"), EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("EFFF01"), EOFValidationError::invalid_prefix); + add_test_case("EF0101", EOFValidationError::invalid_prefix); + add_test_case("EFEF01", EOFValidationError::invalid_prefix); + add_test_case("EFFF01", EOFValidationError::invalid_prefix); - EXPECT_EQ(validate_eof("EF00"), EOFValidationError::eof_version_unknown); + add_test_case("EF00", EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF0001"), EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001", EOFValidationError::section_headers_not_terminated); - // valid except for magic - EXPECT_EQ(validate_eof("EFFF 01 010004 0200010003 030004 00 00000000 600000 AABBCCDD"), - EOFValidationError::invalid_prefix); + add_test_case("EFFF 01 010004 0200010003 030004 00 00800000 600000 AABBCCDD", + EOFValidationError::invalid_prefix, "valid_except_magic"); } -TEST(eof_validation, validate_EOF_version) +TEST_F(eof_validation, validate_EOF_version) { - EXPECT_EQ(validate_eof("EF0002"), EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF00FF"), EOFValidationError::eof_version_unknown); - - // valid except version - EXPECT_EQ(validate_eof("EF0000 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), - EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF0002 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), - EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF00FF 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), - EOFValidationError::eof_version_unknown); + add_test_case("EF0002", EOFValidationError::eof_version_unknown); + add_test_case("EF00FF", EOFValidationError::eof_version_unknown); + + add_test_case("EF0000 010004 0200010003 020004 00 00800000 600000 AABBCCDD", + EOFValidationError::eof_version_unknown, "valid_except_version_00"); + add_test_case("EF0002 010004 0200010003 020004 00 00800000 600000 AABBCCDD", + EOFValidationError::eof_version_unknown, "valid_except_version_02"); + add_test_case("EF00FF 010004 0200010003 020004 00 00800000 600000 AABBCCDD", + EOFValidationError::eof_version_unknown, "valid_except_version_FF"); } -TEST(eof_validation, valid_EOF1_code_pre_shanghai) +TEST_F(eof_validation, minimal_valid_EOF1_code) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 00 00000000 FE", EVMC_PARIS), - EOFValidationError::eof_version_unknown); + add_test_case(eof_bytecode(OP_INVALID), EOFValidationError::success); } -TEST(eof_validation, minimal_valid_EOF1_code) +TEST_F(eof_validation, minimal_valid_EOF1_code_with_data) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), - EOFValidationError::success); + add_test_case(eof_bytecode(OP_INVALID).data("DA"), EOFValidationError::success); } -TEST(eof_validation, minimal_valid_EOF1_code_with_data) +TEST_F(eof_validation, minimal_valid_EOF1_multiple_code_sections) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 00 00000000 FE DA"), - EOFValidationError::success); + add_test_case("EF0001 010008 02000200010001 00 00800000 00800000 FE FE", + EOFValidationError::data_section_missing, "no_data_section"); + add_test_case(eof_bytecode(jumpf(1)).code(OP_INVALID, 0, 0x80, 0).data("DA"), + EOFValidationError::success, "with_data_section"); + + add_test_case(eof_bytecode(OP_PUSH0 + callf(1) + OP_STOP, 1) + .code(OP_POP + callf(2) + OP_POP + OP_RETF, 1, 0, 1) + .code(dup1(OP_ADDRESS) + callf(3) + OP_POP + OP_POP + OP_RETF, 0, 1, 3) + .code(bytecode{OP_DUP1} + OP_RETF, 2, 3, 3), + EOFValidationError::success, "non_void_input_output"); } -TEST(eof_validation, minimal_valid_EOF1_multiple_code_sections) +TEST_F(eof_validation, minimal_valid_EOF1_multiple_container_sections) { - // no data section - EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 00 00000000 00000000 FE FE"), - EOFValidationError::data_section_missing); - // with data section - EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030001 00 00000000 00000000 FE FE DA"), - EOFValidationError::success); - - // non-void input and output types - EXPECT_EQ(validate_eof("EF0001 010010 0200040001000200020002 030000 00 " - "00000000 01000001 00010001 02030003" - "FE 5000 3000 8000"), - EOFValidationError::success); + add_test_case("EF0001 010004 0200010001 0300010001 0300010001 040000 00 00800000 00 00 00", + EOFValidationError::data_section_missing, "no_data_section"); } -TEST(eof_validation, EOF1_types_section_missing) +TEST_F(eof_validation, EOF1_types_section_missing) { - EXPECT_EQ(validate_eof("EF0001 0200010001 00 FE"), EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 0200010001 030001 00 FE DA"), - EOFValidationError::type_section_missing); + add_test_case("EF0001 0200010001 00 FE", EOFValidationError::type_section_missing); + add_test_case("EF0001 0200010001 040001 00 FE DA", EOFValidationError::type_section_missing); } -TEST(eof_validation, EOF1_types_section_0_size) +TEST_F(eof_validation, EOF1_types_section_0_size) { - EXPECT_EQ( - validate_eof("EF0001 010000 0200010001 00 FE"), EOFValidationError::zero_section_size); - EXPECT_EQ(validate_eof("EF0001 010000 0200010001 030001 00 FE DA"), - EOFValidationError::zero_section_size); + add_test_case("EF0001 010000 0200010001 00 FE", EOFValidationError::zero_section_size); + add_test_case( + "EF0001 010000 0200010001 040001 00 FE DA", EOFValidationError::zero_section_size); } -TEST(eof_validation, EOF1_type_section_missing) +TEST_F(eof_validation, EOF1_type_section_missing) { - EXPECT_EQ(validate_eof("EF0001 00"), EOFValidationError::type_section_missing); + add_test_case("EF0001 0200010001 00 FE", EOFValidationError::type_section_missing); + add_test_case("EF0001 0200010001 030001 00 FE DA", EOFValidationError::type_section_missing); + add_test_case("EF0001 00", EOFValidationError::type_section_missing); } -TEST(eof_validation, EOF1_code_section_missing) +TEST_F(eof_validation, EOF1_code_section_missing) { - EXPECT_EQ(validate_eof("EF0001 010004 00"), EOFValidationError::code_section_missing); - EXPECT_EQ(validate_eof("EF0001 010004 030001 00 00000000 DA"), - EOFValidationError::code_section_missing); + add_test_case("EF0001 010004 00", EOFValidationError::code_section_missing); + add_test_case("EF0001 010004 040001 00 00800000 DA", EOFValidationError::code_section_missing); } -TEST(eof_validation, EOF1_code_section_0_size) +TEST_F(eof_validation, EOF1_code_section_0_size) { - EXPECT_EQ(validate_eof("EF0001 010004 020000 00"), EOFValidationError::zero_section_size); - EXPECT_EQ( - validate_eof("EF0001 010004 020000 030001 00 DA"), EOFValidationError::zero_section_size); + add_test_case("EF0001 010004 020000 00", EOFValidationError::zero_section_size); + add_test_case("EF0001 010004 020000 040001 00 DA", EOFValidationError::zero_section_size); } -TEST(eof_validation, EOF1_data_section_0_size) +TEST_F(eof_validation, EOF1_data_section_0_size) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), - EOFValidationError::success); + add_test_case(eof_bytecode(OP_INVALID), EOFValidationError::success); } -TEST(eof_validation, EOF1_data_section_before_code_section) +TEST_F(eof_validation, EOF1_data_section_before_code_section) { - EXPECT_EQ(validate_eof("EF0001 010004 030001 0200010001 00 00000000 AA FE"), + add_test_case("EF0001 010004 030001 0200010001 00 00800000 AA FE", EOFValidationError::code_section_missing); } -TEST(eof_validation, EOF1_data_section_before_types_section) +TEST_F(eof_validation, EOF1_data_section_before_types_section) { - EXPECT_EQ(validate_eof("EF0001 030001 010004 0200010001 00 AA 00000000 FE"), + add_test_case("EF0001 040001 010004 0200010001 00 AA 00800000 FE", EOFValidationError::type_section_missing); } -TEST(eof_validation, EOF1_multiple_data_sections) +TEST_F(eof_validation, EOF1_multiple_data_sections) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 030001 00 00000000 FE DA DA"), + add_test_case("EF0001 010004 0200010001 040001 040001 00 00800000 FE DA DA", EOFValidationError::header_terminator_missing); } -TEST(eof_validation, EOF1_unknown_section) +TEST_F(eof_validation, EOF1_unknown_section) { - EXPECT_EQ(validate_eof("EF0001 040001 00 FE"), EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 FF0001 00 FE"), EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 040001 00 00000000 FE 00"), + add_test_case("EF0001 050001 00 FE", EOFValidationError::type_section_missing); + add_test_case("EF0001 FF0001 00 FE", EOFValidationError::type_section_missing); + add_test_case("EF0001 010004 0200010001 050001 00 00800000 FE 00", EOFValidationError::data_section_missing); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 FF0001 00 00000000 FE 00"), + add_test_case("EF0001 010004 0200010001 FF0001 00 00800000 FE 00", EOFValidationError::data_section_missing); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 040001 00 00000000 FE AA 00"), + add_test_case("EF0001 010004 0200010001 040001 050001 00 00800000 FE AA 00", EOFValidationError::header_terminator_missing); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 FF0001 00 00000000 FE AA 00"), + add_test_case("EF0001 010004 0200010001 040001 FF0001 00 00800000 FE AA 00", EOFValidationError::header_terminator_missing); } -TEST(eof_validation, EOF1_incomplete_section_size) +TEST_F(eof_validation, EOF1_incomplete_section_size) { // TODO: section_headers_not_terminated should rather be incomplete_section_size // in these examples. - EXPECT_EQ(validate_eof("EF0001 01"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 0100"), EOFValidationError::incomplete_section_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200"), EOFValidationError::incomplete_section_number); - EXPECT_EQ(validate_eof("EF0001 010004 02000100"), EOFValidationError::incomplete_section_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001"), - EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 03"), - EOFValidationError::section_headers_not_terminated); - EXPECT_EQ( - validate_eof("EF0001 010004 0200010001 0300"), EOFValidationError::incomplete_section_size); -} - -TEST(eof_validation, EOF1_header_not_terminated) -{ - EXPECT_EQ(validate_eof("EF0001 01"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010004"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010004 FE"), EOFValidationError::code_section_missing); - EXPECT_EQ(validate_eof("EF0001 010004 02"), EOFValidationError::incomplete_section_number); - EXPECT_EQ(validate_eof("EF0001 010004 0200"), EOFValidationError::incomplete_section_number); - EXPECT_EQ( - validate_eof("EF0001 010004 020001"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001"), - EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 FE AA"), - EOFValidationError::header_terminator_missing); + add_test_case("EF0001 01", EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001 0100", EOFValidationError::incomplete_section_size); + add_test_case("EF0001 010004 0200", EOFValidationError::incomplete_section_number); + add_test_case("EF0001 010004 02000100", EOFValidationError::incomplete_section_size); + add_test_case("EF0001 010004 0200010001", EOFValidationError::section_headers_not_terminated); + add_test_case( + "EF0001 010004 0200010001 04", EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001 010004 0200010001 0400", EOFValidationError::incomplete_section_size); } -TEST(eof_validation, EOF1_truncated_section) +TEST_F(eof_validation, EOF1_header_not_terminated) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00"), - EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 000000"), - EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 00000000 FE"), - EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE"), + add_test_case("EF0001 01", EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001 010004", EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001 010004 FE", EOFValidationError::code_section_missing); + add_test_case("EF0001 010004 02", EOFValidationError::incomplete_section_number); + add_test_case("EF0001 010004 0200", EOFValidationError::incomplete_section_number); + add_test_case("EF0001 010004 020001", EOFValidationError::section_headers_not_terminated); + add_test_case( + "EF0001 010004 0200010001 040001", EOFValidationError::section_headers_not_terminated); + add_test_case( + "EF0001 010004 0200010001 040001 FE AA", EOFValidationError::header_terminator_missing); +} + +TEST_F(eof_validation, EOF1_truncated_section) +{ + add_test_case( + "EF0001 010004 0200010002 040000 00", EOFValidationError::invalid_section_bodies_size); + add_test_case("EF0001 010004 0200010002 040000 00 008000", EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE AA"), + add_test_case("EF0001 010004 0200010002 040000 00 00800000 FE", EOFValidationError::invalid_section_bodies_size); + + // Data section may be truncated in runtime subcontainer + add_test_case( + eof_bytecode(returncontract(0, 0, 2), 2).container(eof_bytecode(OP_INVALID).data("", 2)), + ContainerKind::initcode, EOFValidationError::success); + add_test_case( + eof_bytecode(returncontract(0, 0, 1), 2).container(eof_bytecode(OP_INVALID).data("aa", 2)), + ContainerKind::initcode, EOFValidationError::success); + + // Data section may not be truncated in toplevel container + add_test_case( + eof_bytecode(OP_INVALID).data("", 2), EOFValidationError::toplevel_container_truncated); + add_test_case( + eof_bytecode(OP_INVALID).data("aa", 2), EOFValidationError::toplevel_container_truncated); } -TEST(eof_validation, EOF1_code_section_offset) +TEST_F(eof_validation, EOF1_code_section_offset) { - const auto eof = - "EF0001 010008 02000200030001 030004 00 00000001 00000000 6001fe fe 0000 0000"_hex; - ASSERT_EQ(validate_eof(EVMC_CANCUN, eof), EOFValidationError::success); + const auto eof = eof_bytecode(jumpf(1)).code(OP_INVALID, 0, 0x80, 0).data("00000000"); + add_test_case(eof, EOFValidationError::success); - const auto header = read_valid_eof1_header(eof); + const auto header = read_valid_eof1_header(bytecode(eof)); ASSERT_EQ(header.code_sizes.size(), 2); EXPECT_EQ(header.code_sizes[0], 3); EXPECT_EQ(header.code_sizes[1], 1); @@ -233,1017 +222,1171 @@ TEST(eof_validation, EOF1_code_section_offset) EXPECT_EQ(header.code_offsets[1], 28); } -TEST(eof_validation, EOF1_trailing_bytes) +TEST_F(eof_validation, EOF1_trailing_bytes_in_subcontainer) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE DEADBEEF"), + add_test_case( + eof_bytecode(eofcreate() + OP_STOP, 4).container(eof_bytecode(OP_INVALID) + "DEADBEEF"), EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE AABB DEADBEEF"), + add_test_case(eof_bytecode(eofcreate() + OP_STOP, 4) + .container(eof_bytecode(OP_INVALID).data("aabb") + "DEADBEEF"), EOFValidationError::invalid_section_bodies_size); } -TEST(eof_validation, EOF1_no_type_section) +TEST_F(eof_validation, EOF1_trailing_bytes_top_level) { - EXPECT_EQ(validate_eof("EF0001 0200010001 00 FE"), EOFValidationError::type_section_missing); - EXPECT_EQ( - validate_eof("EF0001 02000200010001 00 FE FE"), EOFValidationError::type_section_missing); + add_test_case("EF0001 010004 0200010001 040000 00 00800000 FE DEADBEEF", + EOFValidationError::invalid_section_bodies_size); + add_test_case("EF0001 010004 0200010001 040002 00 00800000 FE AABB DEADBEEF", + EOFValidationError::invalid_section_bodies_size); } -TEST(eof_validation, EOF1_multiple_type_sections) +TEST_F(eof_validation, EOF1_no_type_section) { - EXPECT_EQ(validate_eof("EF0001 010004 010004 02000200010001 00 00000000 00000000 FE FE"), + add_test_case("EF0001 0200010001 00 FE", EOFValidationError::type_section_missing); + add_test_case("EF0001 02000200010001 00 FE FE", EOFValidationError::type_section_missing); +} + +TEST_F(eof_validation, EOF1_multiple_type_sections) +{ + add_test_case("EF0001 010004 010004 02000200010001 00 00800000 00800000 FE FE", EOFValidationError::code_section_missing); // Section order is must be (Types, Code+, Data) - EXPECT_EQ(validate_eof("EF0001 030002 010001 010001 030002 00 0000 FE FE 0000"), + add_test_case("EF0001 030002 010001 010001 040002 00 0000 FE FE 0000", EOFValidationError::type_section_missing); } -TEST(eof_validation, EOF1_type_section_not_first) +TEST_F(eof_validation, EOF1_type_section_not_first) { - EXPECT_EQ(validate_eof("EF0001 0200010001 010004 00 FE 00000000"), - EOFValidationError::type_section_missing); + add_test_case( + "EF0001 0200010001 010004 00 FE 00800000", EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 02000200010001 010004 00 FE FE 00000000"), - EOFValidationError::type_section_missing); + add_test_case( + "EF0001 02000200010001 010004 00 FE FE 00800000", EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 0200010001 010004 030003 00 FE 00000000 AABBCC"), + add_test_case("EF0001 0200010001 010004 040003 00 FE 00800000 AABBCC", EOFValidationError::type_section_missing); - EXPECT_EQ(validate_eof("EF0001 0200010001 030003 010004 00 FE AABBCC 00000000"), + add_test_case("EF0001 0200010001 040003 010004 00 FE AABBCC 00800000", EOFValidationError::type_section_missing); } -TEST(eof_validation, EOF1_invalid_type_section_size) +TEST_F(eof_validation, EOF1_invalid_type_section_size) { - EXPECT_EQ(validate_eof("EF0001 010001 0200010001 030000 00 00 FE"), - EOFValidationError::invalid_type_section_size); - EXPECT_EQ(validate_eof("EF0001 010002 0200010001 030000 00 0000 FE"), + add_test_case( + "EF0001 010001 0200010001 040000 00 00 FE", EOFValidationError::invalid_type_section_size); + add_test_case("EF0001 010002 0200010001 040000 00 0080 FE", EOFValidationError::invalid_type_section_size); - EXPECT_EQ(validate_eof("EF0001 010008 0200010001 030000 00 0000000000000000 FE"), + add_test_case("EF0001 010008 0200010001 040000 00 0080000000000000 FE", EOFValidationError::invalid_type_section_size); - EXPECT_EQ(validate_eof("EF0001 010008 020003000100010001 030000 00 0000000000000000 FE FE FE"), + add_test_case("EF0001 010008 020003000100010001 040000 00 0080000000800000 FE FE FE", EOFValidationError::invalid_type_section_size); - EXPECT_EQ( - validate_eof( - "EF0001 010010 020003000100010001 030000 00 00000000000000000000000000000000 FE FE FE"), + add_test_case( + "EF0001 010010 020003000100010001 040000 00 00800000008000000080000000800000 FE FE FE", EOFValidationError::invalid_type_section_size); } -TEST(eof_validation, EOF1_invalid_section_0_type) +TEST_F(eof_validation, EOF1_invalid_section_0_type) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 00010000 60005C"), + add_test_case("EF0001 010004 0200010001 040000 00 00000000 00", EOFValidationError::invalid_first_section_type); - EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 01000000 5000"), + add_test_case("EF0001 010004 0200010003 040000 00 00010000 60005C", EOFValidationError::invalid_first_section_type); - EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 02030000 60005C"), + add_test_case("EF0001 010004 0200010001 040000 00 01800000 FE", + EOFValidationError::invalid_first_section_type); + add_test_case("EF0001 010004 0200010003 040000 00 02030000 60005C", EOFValidationError::invalid_first_section_type); } -TEST(eof_validation, EOF1_too_many_code_sections) +TEST_F(eof_validation, EOF1_too_many_code_sections) { - const auto valid = "EF0001 011000" + bytecode{"020400"} + 0x400 * bytecode{"0001"} + - "030000 00" + 0x400 * bytecode{"00000000"} + 0x400 * bytecode{"FE"}; - EXPECT_EQ(validate_eof(valid), EOFValidationError::success); + auto eof_code_sections_1024 = eof_bytecode(jumpf(1)); + for (int i = 1; i < 1023; ++i) + eof_code_sections_1024 = + eof_code_sections_1024.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + + auto eof_code_sections_1025 = eof_code_sections_1024; + eof_code_sections_1024 = eof_code_sections_1024.code(OP_STOP, 0, 0x80, 0); + eof_code_sections_1025 = + eof_code_sections_1025.code(jumpf(1024), 0, 0x80, 0).code(OP_STOP, 0, 0x80, 0); + + add_test_case(eof_code_sections_1024, EOFValidationError::success, "valid"); - const auto invalid = "EF0001 011002" + bytecode{"020401"} + 0x401 * bytecode{"0001"} + - "030000 00" + 0x401 * bytecode{"00000000"} + 0x401 * bytecode{"FE"}; - EXPECT_EQ(validate_eof(invalid), EOFValidationError::too_many_code_sections); + add_test_case(eof_code_sections_1025, EOFValidationError::too_many_code_sections, "invalid"); } -TEST(eof_validation, EOF1_undefined_opcodes) +TEST_F(eof_validation, EOF1_undefined_opcodes) { - const auto& gas_table = evmone::instr::gas_costs[EVMC_CANCUN]; + const auto& gas_table = evmone::instr::gas_costs[EVMC_PRAGUE]; for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) { - // PUSH*, DUPN, SWAPN, RJUMP*, CALLF require immediate argument to be valid, - // checked in a separate test. + // PUSH*, DUPN, SWAPN, RJUMP*, CALLF, JUMPF, EOFCREATE, RETRUNCONTRACT require immediate + // argument to be valid, checked in a separate test. if ((opcode >= OP_PUSH1 && opcode <= OP_PUSH32) || opcode == OP_DUPN || - opcode == OP_SWAPN || opcode == OP_RJUMP || opcode == OP_RJUMPI || - opcode == OP_RJUMPV || opcode == OP_CALLF) + opcode == OP_SWAPN || opcode == OP_EXCHANGE || opcode == OP_RJUMP || + opcode == OP_RJUMPI || opcode == OP_CALLF || opcode == OP_RJUMPV || + opcode == OP_DATALOADN || opcode == OP_JUMPF || opcode == OP_EOFCREATE || + opcode == OP_RETURNCONTRACT) continue; - // These opcodes are deprecated since Cancun. + // These opcodes are deprecated since Prague. // gas_cost table current implementation does not allow to undef instructions. if (opcode == OP_JUMP || opcode == OP_JUMPI || opcode == OP_PC || opcode == OP_CALLCODE || - opcode == OP_SELFDESTRUCT) + opcode == OP_SELFDESTRUCT || opcode == OP_CALL || opcode == OP_STATICCALL || + opcode == OP_DELEGATECALL || opcode == OP_CREATE || opcode == OP_CREATE2 || + opcode == OP_CODESIZE || opcode == OP_CODECOPY || opcode == OP_EXTCODESIZE || + opcode == OP_EXTCODECOPY || opcode == OP_EXTCODEHASH || opcode == OP_GAS) continue; - auto cont = - "EF0001 010004 0200010014 030000 00 00000000 6001" - "80808080808080808080808080808080 " - ""_hex; + const auto expected = (gas_table[opcode] == evmone::instr::undefined ? + EOFValidationError::undefined_instruction : + EOFValidationError::success); if (opcode == OP_RETF) { - cont += "5050505050505050505050505050505050"_hex; - cont += static_cast(opcode); - cont[10] = 0x24; + // RETF can be tested in 2nd code section. + add_test_case(eof_bytecode(callf(1) + OP_STOP).code(OP_RETF, 0, 0, 0), expected); } else { - cont += static_cast(opcode); + auto op_stack_change = instr::traits[opcode].stack_height_change; + auto code = push(1) + 16 * OP_DUP1 + Opcode{static_cast(opcode)}; if (!instr::traits[opcode].is_terminating) - cont += "00"_hex; - else - cont[10] = 0x13; + code += bytecode{OP_STOP}; + add_test_case( + eof_bytecode(code, static_cast(std::max(17, 17 + op_stack_change))), + expected); } - - auto op_stack_change = instr::traits[opcode].stack_height_change; - cont[18] = static_cast(op_stack_change <= 0 ? 17 : 17 + op_stack_change); - - const auto expected = (gas_table[opcode] == evmone::instr::undefined ? - EOFValidationError::undefined_instruction : - EOFValidationError::success); - auto result = validate_eof(cont); - EXPECT_EQ(result, expected) << hex(cont); } - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), - EOFValidationError::success); + add_test_case(eof_bytecode(OP_INVALID), EOFValidationError::success); } -TEST(eof_validation, EOF1_truncated_push) +TEST_F(eof_validation, EOF1_truncated_push) { - auto eof_header = "EF0001 010004 0200010001 030000 00 00000000"_hex; - auto& code_size_byte = eof_header[10]; for (uint8_t opcode = OP_PUSH1; opcode <= OP_PUSH32; ++opcode) { const auto required_bytes = static_cast(opcode) - OP_PUSH1 + 1; for (size_t i = 0; i < required_bytes; ++i) { + auto eof_header = "EF0001 010004 0200010001 040000 00 00800000"_hex; + auto& code_size_byte = eof_header[10]; const bytes code{opcode + bytes(i, 0)}; code_size_byte = static_cast(code.size()); const auto container = eof_header + code; - EXPECT_EQ(validate_eof(container), EOFValidationError::truncated_instruction) - << hex(container); + add_test_case(container, EOFValidationError::truncated_instruction); } - const bytes code{opcode + bytes(required_bytes, 0) + uint8_t{OP_STOP}}; - code_size_byte = static_cast(code.size()); - - eof_header[18] = static_cast(instr::traits[opcode].stack_height_change); + const auto container = eof_bytecode(opcode + bytes(required_bytes, 0) + OP_STOP, + static_cast(instr::traits[opcode].stack_height_change)); - const auto container = eof_header + code; - - EXPECT_EQ(validate_eof(container), EOFValidationError::success) << hex(container); + add_test_case(container, EOFValidationError::success); } } -TEST(eof_validation, EOF1_valid_rjump) +TEST_F(eof_validation, EOF1_valid_rjump) { // offset = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000000"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjump(0) + OP_STOP), EOFValidationError::success, "offset_zero"); // offset = 3 - EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 5C00036001005CFFFA"), - EOFValidationError::success); + add_test_case( + eof_bytecode( + rjumpi(5, OP_PUSH0) + OP_PUSH0 + OP_PUSH0 + rjump(3) + OP_PUSH0 + push(1) + OP_STOP, 2), + EOFValidationError::success, "offset_positive"); // offset = -4 - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5B5CFFFC"), - EOFValidationError::success); + add_test_case( + eof_bytecode(OP_JUMPDEST + rjump(-4)), EOFValidationError::success, "offset_negative"); } -TEST(eof_validation, EOF1_valid_rjumpi) +TEST_F(eof_validation, EOF1_valid_rjumpi) { // offset = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000001 60005D000000"), - EOFValidationError::success); + add_test_case( + eof_bytecode(rjumpi(0, 0) + OP_STOP, 1), EOFValidationError::success, "offset_zero"); // offset = 3 - EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 60005D00035B5B5B00"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjumpi(3, 0) + OP_JUMPDEST + OP_JUMPDEST + OP_JUMPDEST + OP_STOP, 1), + EOFValidationError::success, "offset_positive"); // offset = -5 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000001 60005DFFFB00"), - EOFValidationError::success); + add_test_case( + eof_bytecode(rjumpi(-5, 0) + OP_STOP, 1), EOFValidationError::success, "offset_negative"); } -TEST(eof_validation, EOF1_valid_rjumpv) +TEST_F(eof_validation, EOF1_valid_rjumpv) { // table = [0] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 60005E010000600100"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjumpv({0}, 0) + push(1) + OP_STOP, 1), EOFValidationError::success, + "single_entry_case_0"); // table = [0,3] case = 0 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000E 030000 00 00000001 60005E0200000003600100600200"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjumpv({0, 3}, 0) + push(1) + OP_STOP + push(2) + OP_STOP, 1), + EOFValidationError::success, "two_entries_case_0"); // table = [0,3] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000E 030000 00 00000001 60025E0200000003600100600200"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjumpv({0, 3}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), + EOFValidationError::success, "two_entries_case_2"); // table = [0,3,-10] case = 2 - EXPECT_EQ(validate_eof( - "EF0001 010004 0200010010 030000 00 00000001 60025E0300000003FFF6600100600200"), - EOFValidationError::success); + add_test_case(eof_bytecode(rjumpv({0, 3, -10}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), + EOFValidationError::success, "three_entries_case_2"); } -TEST(eof_validation, EOF1_rjump_truncated) +TEST_F(eof_validation, EOF1_rjump_truncated) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 5C"), + add_test_case("EF0001 010004 0200010001 040000 00 00800000 E0", EOFValidationError::truncated_instruction); - EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 00000000 5C00"), + add_test_case("EF0001 010004 0200010002 040000 00 00800000 E000", EOFValidationError::truncated_instruction); } -TEST(eof_validation, EOF1_rjumpi_truncated) +TEST_F(eof_validation, EOF1_rjumpi_truncated) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 00000000 60005D"), + add_test_case("EF0001 010004 0200010003 040000 00 00800000 6000E1", EOFValidationError::truncated_instruction); - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 60005D00"), + add_test_case("EF0001 010004 0200010004 040000 00 00800000 6000E100", EOFValidationError::truncated_instruction); } -TEST(eof_validation, EOF1_rjumpv_truncated) +TEST_F(eof_validation, EOF1_rjumpv_truncated) { // table = [0] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010005 030000 00 00000000 60005E0100"), + add_test_case("EF0001 010004 0200010005 040000 00 00800000 6000E20000", EOFValidationError::truncated_instruction); // table = [0,3] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010007 030000 00 00000000 60005E02000000"), + add_test_case("EF0001 010004 0200010007 040000 00 00800000 6000E201000000", EOFValidationError::truncated_instruction); // table = [0,3] case = 2 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60025E020000"), + add_test_case("EF0001 010004 0200010006 040000 00 00800000 6002E2010000", EOFValidationError::truncated_instruction); // table = [0,3,-10] case = 2 - EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000000 60025E0300000003FF"), + add_test_case("EF0001 010004 0200010009 040000 00 00800000 6002E20200000003FF", EOFValidationError::truncated_instruction); } -TEST(eof_validation, EOF1_rjumpv_0_count) -{ - auto code = eof1_bytecode(rjumpv({}, 0) + OP_STOP, 1); - - EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_rjumpv_count); -} - -TEST(eof_validation, EOF1_rjump_invalid_destination) +TEST_F(eof_validation, EOF1_rjump_invalid_destination) { // Into header (offset = -5) - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFFB00"), - EOFValidationError::invalid_rjump_destination); + add_test_case(eof_bytecode(rjump(-5) + OP_STOP), EOFValidationError::invalid_rjump_destination); // To before code begin (offset = -13) - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFF300"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjump(-13) + OP_STOP), EOFValidationError::invalid_rjump_destination); // To after code end (offset = 2) - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000200"), - EOFValidationError::invalid_rjump_destination); + add_test_case(eof_bytecode(rjump(2) + OP_STOP), EOFValidationError::invalid_rjump_destination); // To code end (offset = 1) - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000100"), - EOFValidationError::invalid_rjump_destination); + add_test_case(eof_bytecode(rjump(1) + OP_STOP), EOFValidationError::invalid_rjump_destination); // To the same RJUMP immediate (offset = -1) - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFFF00"), - EOFValidationError::invalid_rjump_destination); + add_test_case(eof_bytecode(rjump(-1) + OP_STOP), EOFValidationError::invalid_rjump_destination); // To PUSH immediate (offset = -4) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005CFFFC00"), + add_test_case( + eof_bytecode(push(0) + rjump(-4) + OP_STOP), EOFValidationError::invalid_rjump_destination); + + // To EOFCREATE immediate + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode(rjump(9) + 0 + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0} + OP_POP + OP_STOP, 4) + .container(embedded), EOFValidationError::invalid_rjump_destination); + + // To RETURNCONTRACT immediate + add_test_case( + eof_bytecode(rjump(5) + 0 + 0 + OP_RETURNCONTRACT + Opcode{0}, 2).container(embedded), + ContainerKind::initcode, EOFValidationError::invalid_rjump_destination); } -TEST(eof_validation, EOF1_rjumpi_invalid_destination) +TEST_F(eof_validation, EOF1_rjumpi_invalid_destination) { // Into header (offset = -7) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFF900"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjumpi(-7, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // To before code begin (offset = -15) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFF100"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjumpi(-15, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // To after code end (offset = 2) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005D000200"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjumpi(2, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // To code end (offset = 1) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005D000100"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjumpi(1, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // To the same RJUMPI immediate (offset = -1) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFFF00"), - EOFValidationError::invalid_rjump_destination); + add_test_case( + eof_bytecode(rjumpi(-1, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // To PUSH immediate (offset = -4) - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFFC00"), + add_test_case( + eof_bytecode(rjumpi(-4, 0) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); + + // To EOFCREATE immediate + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode( + rjumpi(9, 0) + 0 + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0} + OP_POP + OP_STOP, 4) + .container(embedded), EOFValidationError::invalid_rjump_destination); + + // To RETURNCONTRACT immediate + add_test_case( + eof_bytecode(rjumpi(5, 0) + 0 + 0 + OP_RETURNCONTRACT + Opcode{0}, 2).container(embedded), + ContainerKind::initcode, EOFValidationError::invalid_rjump_destination); } -TEST(eof_validation, EOF1_rjumpv_invalid_destination) +TEST_F(eof_validation, EOF1_rjumpv_invalid_destination) { // table = [-23] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFE96001"), + add_test_case(eof_bytecode(rjumpv({-23}, 0) + push(1) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // table = [-8] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFF86001"), + add_test_case(eof_bytecode(rjumpv({-8}, 0) + push(1) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // table = [-1] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFFF6001"), + add_test_case(eof_bytecode(rjumpv({-1}, 0) + push(1) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); - // table = [2] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E0100026001"), + // table = [3] case = 0 + add_test_case(eof_bytecode(rjumpv({3}, 0) + push(1) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); - // table = [3] case = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E0100036001"), + // table = [4] case = 0 + add_test_case(eof_bytecode(rjumpv({4}, 0) + push(1) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // table = [0,3,-27] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFE56001006002"), + add_test_case(eof_bytecode(rjumpv({0, 3, -27}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // table = [0,3,-12] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFF46001006002"), + add_test_case(eof_bytecode(rjumpv({0, 3, -12}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); // table = [0,3,-1] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFFF6001006002"), + add_test_case(eof_bytecode(rjumpv({0, 3, -1}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), + EOFValidationError::invalid_rjump_destination); + + // table = [0,3,6] case = 2 + add_test_case(eof_bytecode(rjumpv({0, 3, 6}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); - // table = [0,3,5] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E030000000300056001006002"), + // table = [0,3,7] case = 2 + add_test_case(eof_bytecode(rjumpv({0, 3, 7}, 2) + push(1) + OP_STOP + push(2) + OP_STOP, 1), EOFValidationError::invalid_rjump_destination); - // table = [0,3,6] case = 2 - EXPECT_EQ( - validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E030000000300066001006002"), + // To EOFCREATE immediate + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode( + rjumpv({9}, 0) + 0 + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0} + OP_POP + OP_STOP, 4) + .container(embedded), EOFValidationError::invalid_rjump_destination); + + // To RETURNCONTRACT immediate + add_test_case( + eof_bytecode(rjumpv({5}, 0) + 0 + 0 + OP_RETURNCONTRACT + Opcode{0}, 2).container(embedded), + ContainerKind::initcode, EOFValidationError::invalid_rjump_destination); } -TEST(eof_validation, EOF1_section_order) +TEST_F(eof_validation, EOF1_section_order) { - // 01 02 03 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030002 00 00000001 60005D000000 AABB"), + // 01 02 04 + add_test_case("EF0001 010004 0200010006 040002 00 00800001 6000E0000000 AABB", EOFValidationError::success); - // 01 03 02 - EXPECT_EQ(validate_eof("EF0001 010004 030002 0200010006 00 00000000 AABB 60005D000000"), + // 01 04 02 + add_test_case("EF0001 010004 040002 0200010006 00 00800000 AABB 6000E0000000", EOFValidationError::code_section_missing); - // 02 01 03 - EXPECT_EQ(validate_eof("EF0001 0200010006 010004 030002 00 60005D000000 00000000 AABB"), + // 02 01 04 + add_test_case("EF0001 0200010006 010004 040002 00 6000E0000000 00800000 AABB", EOFValidationError::type_section_missing); - // 02 03 01 - EXPECT_EQ(validate_eof("EF0001 0200010006 030002 010004 00 60005D000000 AABB 00000000"), + // 02 04 01 + add_test_case("EF0001 0200010006 040002 010004 00 6000E0000000 AABB 00800000", EOFValidationError::type_section_missing); - // 03 01 02 - EXPECT_EQ(validate_eof("EF0001 030002 010004 0200010006 00 AABB 00000000 60005D000000"), + // 04 01 02 + add_test_case("EF0001 040002 010004 0200010006 00 AABB 00800000 6000E0000000", EOFValidationError::type_section_missing); - // 03 02 01 - EXPECT_EQ(validate_eof("EF0001 030002 0200010006 010004 00 AABB 60005D000000 00000000"), + // 04 02 01 + add_test_case("EF0001 040002 0200010006 010004 00 AABB 6000E0000000 00800000", EOFValidationError::type_section_missing); + + // 01 02 03 04 + add_test_case( + "EF0001 010004 0200010007 0300010014 040002 00 00800004 5F5F5F5FEC0000 " + "EF000101000402000100010400000000800000FE AABB", + EOFValidationError::success); + + // 03 01 02 04 + add_test_case( + "EF0001 0300010014 010004 0200010007 040002 00 EF000101000402000100010400000000800000FE " + "00800004 5F5F5F5FEC0000 AABB", + EOFValidationError::type_section_missing); + + // 01 03 02 04 + add_test_case( + "EF0001 010004 0300010014 0200010007 040002 00 00800004 " + "EF000101000402000100010400000000800000FE 5F5F5F5FEC0000 AABB", + EOFValidationError::code_section_missing); + + // 01 02 04 03 + add_test_case( + "EF0001 010004 0200010007 040002 0300010014 00 00800004 5F5F5F5FEC0000 AABB " + "EF000101000402000100010400000000800000FE", + EOFValidationError::header_terminator_missing); } -TEST(eof_validation, deprecated_instructions) +TEST_F(eof_validation, deprecated_instructions) { - for (auto op : {OP_CALLCODE, OP_SELFDESTRUCT, OP_JUMP, OP_JUMPI, OP_PC}) - { - EXPECT_EQ(validate_eof(eof1_bytecode(op)), EOFValidationError::undefined_instruction); - } + for (auto op : {OP_CALLCODE, OP_SELFDESTRUCT, OP_JUMP, OP_JUMPI, OP_PC, OP_CALL, OP_STATICCALL, + OP_DELEGATECALL, OP_CREATE, OP_CREATE2, OP_CODESIZE, OP_CODECOPY, OP_EXTCODESIZE, + OP_EXTCODECOPY, OP_EXTCODEHASH, OP_GAS}) + add_test_case(eof_bytecode(op), EOFValidationError::undefined_instruction); } -TEST(eof_valication, max_arguments_count) +TEST_F(eof_validation, max_arguments_count) { - EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030000 00 00000000 7F7F007F B1 B1"), + add_test_case( + eof_bytecode(127 * push0() + callf(1) + OP_STOP, 127).code(OP_RETF, 127, 127, 127), EOFValidationError::success); - EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030000 00 00000000 80800080 B1 B1"), + add_test_case(eof_bytecode(callf(1) + OP_STOP, 0).code(OP_RETF, 128, 128, 128), EOFValidationError::inputs_outputs_num_above_limit); - { - auto code = "EF0001 010008 020002000100FF 030000 00 00000000 007F007F B1" + - 127 * bytecode{1} + OP_RETF; + add_test_case(eof_bytecode(callf(1) + OP_STOP, 127).code(127 * push(1) + OP_RETF, 0, 127, 127), + EOFValidationError::success); - EXPECT_EQ(validate_eof(code), EOFValidationError::success); - } + add_test_case(eof_bytecode(callf(1) + OP_STOP, 129).code(129 * push(1) + OP_RETF, 0, 129, 129), + EOFValidationError::inputs_outputs_num_above_limit); - { - auto code = "EF0001 010008 02000200010101 030000 00 00000000 00800080 B1" + - 128 * bytecode{1} + OP_RETF; + add_test_case(eof_bytecode(127 * push0() + callf(1) + OP_STOP, 127) + .code(127 * OP_POP + OP_RETF, 127, 0, 127), + EOFValidationError::success); - EXPECT_EQ(validate_eof(code), EOFValidationError::inputs_outputs_num_above_limit); - } + add_test_case(eof_bytecode(128 * push(1) + callf(1) + OP_STOP, 128) + .code(128 * OP_POP + OP_RETF, 128, 0, 128), + EOFValidationError::inputs_outputs_num_above_limit); +} - { - auto code = - "EF0001 010008 02000200010080 030000 00 00000000 7F00007F B1" + 127 * OP_POP + OP_RETF; +TEST_F(eof_validation, max_stack_height) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP, 0) + .code(0x3FF * push(1) + 0x3FF * OP_POP + OP_RETF, 0, 0, 1023), + EOFValidationError::success); - EXPECT_EQ(validate_eof(code), EOFValidationError::success); - } + add_test_case(eof_bytecode(1023 * push(1) + 1023 * OP_POP + callf(1) + OP_STOP, 1023) + .code(OP_RETF, 0, 0, 0), + EOFValidationError::success); - { - auto code = - "EF0001 010008 02000200010081 030000 00 00000000 80000080 B1" + 128 * OP_POP + OP_RETF; + add_test_case(eof_bytecode(1024 * push(1) + OP_STOP, 1024), + EOFValidationError::max_stack_height_above_limit); - EXPECT_EQ(validate_eof(code), EOFValidationError::inputs_outputs_num_above_limit); - } + add_test_case(eof_bytecode(0x400 * push(1) + callf(1) + 0x400 * OP_POP + OP_STOP, 1024) + .code(OP_RETF, 0, 0, 0), + EOFValidationError::max_stack_height_above_limit); + + add_test_case(eof_bytecode(callf(1) + OP_STOP, 0) + .code(0x400 * push(1) + 0x400 * OP_POP + OP_RETF, 0, 0, 1023), + EOFValidationError::invalid_max_stack_height); + + add_test_case(eof_bytecode(1024 * push(1) + callf(1) + 1024 * OP_POP + OP_STOP, 1023) + .code(OP_RETF, 0, 0, 0), + EOFValidationError::invalid_max_stack_height); + + add_test_case(eof_bytecode(rjumpi(2, 0) + 1 + OP_STOP, 1), EOFValidationError::success); + + add_test_case( + eof_bytecode(rjumpi(-3, 0) + OP_STOP, 1), EOFValidationError::stack_height_mismatch); + + add_test_case( + eof_bytecode(rjumpv({-4}, 0) + OP_STOP, 1), EOFValidationError::stack_height_mismatch); } -TEST(eof_valication, max_stack_heigh) +TEST_F(eof_validation, EOF1_callf_truncated) { - { - auto code = "EF0001 010008 02000200010BFE 030000 00 00000000 000003FF B1" + - 0x3FF * bytecode{1} + 0x3FF * OP_POP + OP_RETF; + add_test_case("EF0001 010004 0200010001 040000 00 00800000 E3", + EOFValidationError::truncated_instruction); - EXPECT_EQ(validate_eof(code), EOFValidationError::success); - } + add_test_case("EF0001 010004 0200010002 040000 00 00800000 E300", + EOFValidationError::truncated_instruction); +} - { - auto code = "EF0001 010008 0200020BFE0001 030000 00 000003FF 00000000" + - 0x3FF * bytecode{1} + 0x3FF * OP_POP + OP_RETF + OP_RETF; +TEST_F(eof_validation, callf_invalid_code_section_index) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP), EOFValidationError::invalid_code_section_index); +} - EXPECT_EQ(validate_eof(code), EOFValidationError::success); - } +TEST_F(eof_validation, incomplete_section_size) +{ + add_test_case("ef0001 010100 02003f 0100", EOFValidationError::incomplete_section_size); +} - { - auto code = "EF0001 010008 02000200010C01 030000 00 00000000 00000400 B1" + - 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF; +TEST_F(eof_validation, data_section_missing) +{ + add_test_case( + "ef0001 010004 0200010001 00 00800000 fe", EOFValidationError::data_section_missing); +} - EXPECT_EQ(validate_eof(code), EOFValidationError::max_stack_height_above_limit); - } +TEST_F(eof_validation, multiple_code_sections_headers) +{ + add_test_case( + "0xef0001 010008 020001 0004 020001 0005 040000 00 00800000 045c0000 00405c00 00002e0005", + EOFValidationError::data_section_missing); +} - { - auto code = "EF0001 010008 0200020C010001 030000 00 00000400 00000000" + - 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF + OP_RETF; +TEST_F(eof_validation, many_code_sections_1023) +{ + auto code = eof_bytecode(jumpf(1)); + for (auto i = 1; i < 1022; ++i) + code = code.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + code = code.code(OP_STOP, 0, 0x80, 0); - EXPECT_EQ(validate_eof(code), EOFValidationError::max_stack_height_above_limit); - } + add_test_case(code, EOFValidationError::success); +} - { - auto code = "EF0001 010008 02000200010C01 030000 00 00000000 000003FF B1" + - 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF; +TEST_F(eof_validation, many_code_sections_1024) +{ + auto code = eof_bytecode(jumpf(1)); + for (auto i = 1; i < 1023; ++i) + code = code.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + code = code.code(OP_STOP, 0, 0x80, 0); - EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_max_stack_height); - } + add_test_case(code, EOFValidationError::success); +} - { - auto code = "EF0001 010008 0200020C010001 030000 00 000003FF 00000000" + - 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF + OP_RETF; +TEST_F(eof_validation, too_many_code_sections) +{ + auto code = eof_bytecode(jumpf(1)); + for (auto i = 1; i < 1024; ++i) + code = code.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + code = code.code(OP_STOP, 0, 0x80, 0); - EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_max_stack_height); - } + add_test_case(code, EOFValidationError::too_many_code_sections); +} - { - auto code = eof1_bytecode(rjumpi(2, 0) + 1 + OP_RETF, 1); +TEST_F(eof_validation, EOF1_dataloadn_truncated) +{ + add_test_case("EF0001 010004 0200010001 040000 00 00800000 D1", + EOFValidationError::truncated_instruction); - EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); - } + add_test_case("EF0001 010004 0200010002 040000 00 00800000 D100", + EOFValidationError::truncated_instruction); +} + +TEST_F(eof_validation, dataloadn) +{ + // DATALOADN{0} + add_test_case(eof_bytecode(dataloadn(0) + OP_POP + OP_STOP, 1) + .data("0000000000000000111111111111111122222222222222223333333333333333"), + EOFValidationError::success); + + // DATALOADN{1} + add_test_case(eof_bytecode(dataloadn(1) + OP_POP + OP_STOP, 1) + .data("000000000000000011111111111111112222222222222222333333333333333344"), + EOFValidationError::success); + + // DATALOADN{32} + add_test_case(eof_bytecode(dataloadn(32) + OP_POP + OP_STOP, 1) + .data("0000000000000000111111111111111122222222222222223333333333333333" + "0000000000000000111111111111111122222222222222223333333333333333"), + EOFValidationError::success); + + // DATALOADN{0} - no data section + add_test_case(eof_bytecode(dataloadn(0) + OP_POP + OP_STOP, 1), + EOFValidationError::invalid_dataloadn_index); + + // DATALOADN{1} - out of data section bounds + add_test_case(eof_bytecode(dataloadn(1) + OP_POP + OP_STOP, 1).data("00"), + EOFValidationError::invalid_dataloadn_index); + + // DATALOADN{32} - out of data section bounds + add_test_case(eof_bytecode(dataloadn(32) + OP_POP + OP_STOP, 1) + .data("0000000000000000111111111111111122222222222222223333333333333333"), + EOFValidationError::invalid_dataloadn_index); + + // DATALOADN{uint16_max} - out of data section bounds + add_test_case(eof_bytecode(dataloadn(0xffff) + OP_POP + OP_STOP, 1) + .data("0000000000000000111111111111111122222222222222223333333333333333"), + EOFValidationError::invalid_dataloadn_index); + + // DATALOADN{32} - truncated word + add_test_case(eof_bytecode(dataloadn(32) + OP_POP + OP_STOP, 1) + .data("0000000000000000111111111111111122222222222222223333333333333333 " + "00000000000000001111111111111111222222222222222233333333333333"), + EOFValidationError::invalid_dataloadn_index); +} + +TEST_F(eof_validation, non_returning_status) +{ + // Non-returning with no JUMPF and no RETF + add_test_case(eof_bytecode(OP_STOP), EOFValidationError::success); + // Non-returning with JUMPF + add_test_case(eof_bytecode(jumpf(1)).code(OP_STOP, 0, 0x80, 0), EOFValidationError::success); + + // Returning with RETF + add_test_case( + eof_bytecode(callf(1) + OP_STOP).code(OP_RETF, 0, 0, 0), EOFValidationError::success); + // Returning with JUMPF + add_test_case(eof_bytecode(callf(1) + OP_STOP).code(jumpf(2), 0, 0, 0).code(OP_RETF, 0, 0, 0), + EOFValidationError::success); + // Returning with JUMPF to returning and RETF + add_test_case(eof_bytecode(OP_PUSH0 + callf(1) + OP_STOP, 1) + .code(bytecode{OP_RJUMPI} + "0001" + OP_RETF + jumpf(2), 1, 0, 1) + .code(OP_RETF, 0, 0, 0), + EOFValidationError::success); + // Returning with JUMPF to non-returning and RETF + add_test_case(eof_bytecode(OP_PUSH0 + callf(1) + OP_STOP, 1) + .code(bytecode{OP_RJUMPI} + "0001" + OP_RETF + jumpf(0), 1, 0, 1), + EOFValidationError::success); + + // Invalid with RETF + add_test_case(eof_bytecode(jumpf(1)).code(OP_RETF, 0, 0x80, 0), + EOFValidationError::invalid_non_returning_flag); + add_test_case(eof_bytecode(OP_RETF), EOFValidationError::invalid_non_returning_flag); + // Invalid with JUMPF to returning + add_test_case(eof_bytecode(jumpf(1)).code(jumpf(2), 0, 0x80, 0).code(OP_RETF, 0, 0, 0), + EOFValidationError::invalid_non_returning_flag); + // Invalid with JUMPF to non-returning + add_test_case(eof_bytecode(jumpf(1)).code(jumpf(0), 0, 0, 0), + EOFValidationError::invalid_non_returning_flag); + // Invalid with JUMPF to returning and RETF + add_test_case(eof_bytecode(OP_PUSH0 + jumpf(1), 1) + .code(bytecode{OP_RJUMPI} + "0001" + OP_RETF + jumpf(2), 1, 0x80, 1) + .code(OP_RETF, 0, 0, 0), + EOFValidationError::invalid_non_returning_flag); + // Invalid with JUMPF to non-returning and RETF + add_test_case(eof_bytecode(OP_PUSH0 + jumpf(1), 1) + .code(bytecode{OP_RJUMPI} + "0001" + OP_RETF + jumpf(0), 1, 0x80, 1), + EOFValidationError::invalid_non_returning_flag); + + // Circular JUMPF: can be both returning and non-returning + add_test_case(eof_bytecode(jumpf(1)).code(jumpf(2), 0, 0x80, 0).code(jumpf(1), 0, 0x80, 0), + EOFValidationError::success); + add_test_case(eof_bytecode(callf(1) + OP_STOP).code(jumpf(2), 0, 0, 0).code(jumpf(1), 0, 0, 0), + EOFValidationError::success); +} + +TEST_F(eof_validation, callf_into_nonreturning) +{ + // function 0: (0, non-returning) : CALLF{1} STOP + // function 2: (1, non-returning) : STOP + add_test_case(eof_bytecode(callf(1) + OP_STOP).code(OP_STOP, 0, 0x80, 0), + EOFValidationError::callf_to_non_returning_function); +} + +TEST_F(eof_validation, jumpf_equal_outputs) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP, 3) + .code(jumpf(2), 0, 3, 0) + .code(3 * OP_PUSH0 + OP_RETF, 0, 3, 3), + EOFValidationError::success); +} + +TEST_F(eof_validation, jumpf_compatible_outputs) +{ + add_test_case(eof_bytecode(callf(1) + OP_STOP, 5) + .code(2 * OP_PUSH0 + jumpf(2), 0, 5, 2) + .code(3 * OP_PUSH0 + OP_RETF, 0, 3, 3), + EOFValidationError::success); +} + +TEST_F(eof_validation, jumpf_incompatible_outputs) +{ + const auto code = eof_bytecode(callf(1) + OP_STOP, 3) + .code(jumpf(2), 0, 3, 0) + .code(5 * OP_PUSH0 + OP_RETF, 0, 5, 3); + + add_test_case(code, EOFValidationError::jumpf_destination_incompatible_outputs); +} + +TEST_F(eof_validation, unreachable_code_sections) +{ + add_test_case(eof_bytecode(OP_INVALID).code(OP_INVALID, 0, 0x80, 0), + EOFValidationError::unreachable_code_sections); + + add_test_case(eof_bytecode(callf(1) + OP_STOP, 0) + .code(bytecode{"5B"} + OP_RETF, 0, 0, 0) + .code(bytecode{"FE"}, 0, 0x80, 0), + EOFValidationError::unreachable_code_sections); + + + add_test_case(eof_bytecode(callf(2) + OP_STOP, 0) + .code(bytecode{"FE"}, 0, 0x80, 0) + .code(bytecode{"5B"} + OP_RETF, 0, 0, 0), + EOFValidationError::unreachable_code_sections); + + add_test_case(eof_bytecode(callf(3) + OP_STOP, 0) + .code(bytecode{"FE"}, 0, 0x80, 0) + .code(bytecode{"5B"} + OP_RETF, 0, 0, 0) + .code(callf(2) + OP_RETF, 0, 0, 0), + EOFValidationError::unreachable_code_sections); + + add_test_case(eof_bytecode(jumpf(0)).code(jumpf(1), 0, 0x80, 0), + EOFValidationError::unreachable_code_sections); + + add_test_case(eof_bytecode(jumpf(1)) + .code(bytecode{OP_STOP}, 0, 0x80, 0) + .code(bytecode{"5B"} + OP_RETF, 0, 0, 0), + EOFValidationError::unreachable_code_sections); { - auto code = eof1_bytecode(rjumpi(-3, 0) + OP_RETF, 1); + auto code_sections_256_err_001 = eof_bytecode(jumpf(1)).code(jumpf(1), 0, 0x80, 0); + auto code_sections_256_err_254 = eof_bytecode(jumpf(1)).code(jumpf(2), 0, 0x80, 0); + for (int i = 2; i < 254; ++i) + { + code_sections_256_err_001.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + code_sections_256_err_254.code(jumpf(static_cast(i + 1)), 0, 0x80, 0); + } - EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); + code_sections_256_err_001.code(jumpf(255), 0, 0x80, 0) + .code(3 * bytecode{"5B"} + OP_STOP, 0, 0x80, 0); + code_sections_256_err_254.code(jumpf(254), 0, 0x80, 0) + .code(3 * bytecode{"5B"} + OP_STOP, 0, 0x80, 0); + + // Code Section 1 calls itself instead of code section 2, leaving code section 2 unreachable + add_test_case(code_sections_256_err_001, EOFValidationError::unreachable_code_sections); + + // Code Section 254 calls itself instead of code section 255, leaving code section 255 + // unreachable + add_test_case(code_sections_256_err_254, EOFValidationError::unreachable_code_sections); + + // Code Section 0 calls section 1, which calls itself, leaving section + // 2 unreachable + add_test_case(eof_bytecode(jumpf(1)).code(jumpf(1), 0, 0x80, 0).code(jumpf(2), 0, 0x80, 0), + EOFValidationError::unreachable_code_sections); + + // Code Section 0 calls section 1, which calls section 2, section 3 and + // 4 call each other but are not reachable from section 0 + add_test_case(eof_bytecode(jumpf(1)) + .code(jumpf(2), 0, 0x80, 0) + .code(OP_INVALID, 0, 0x80, 0) + .code(jumpf(4), 0, 0x80, 0) + .code(jumpf(3), 0, 0x80, 0), + EOFValidationError::unreachable_code_sections); } +} + +TEST_F(eof_validation, EOF1_embedded_container) +{ + // no data section + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode(eofcreate() + OP_STOP, 4).container(embedded), EOFValidationError::success); + + // no data section in container, but anticipated aux_data + // data section is allowed to be truncated in runtime subcontainer + add_test_case( + eof_bytecode(returncontract(0, 0, 2), 2).container(eof_bytecode(OP_INVALID).data("", 2)), + ContainerKind::initcode, EOFValidationError::success); + + // with data section + add_test_case(eof_bytecode(eofcreate() + OP_STOP, 4).container(embedded).data("AABB", 2), + EOFValidationError::success); + + // garbage in container section - not allowed + add_test_case(eof_bytecode(eofcreate() + OP_STOP, 4).container("aabbccddeeff"), + EOFValidationError::invalid_prefix); + + // multiple container sections + add_test_case(eof_bytecode(eofcreate() + OP_POP + eofcreate().container(1) + OP_STOP, 4) + .container(embedded) + .container(eof_bytecode(revert(0, 0), 2)), + EOFValidationError::success); + + // Max number (256) of container sections + bytecode code; + for (auto i = 0; i < 256; ++i) + code += eofcreate().container(static_cast(i)) + OP_POP; + code += bytecode{OP_STOP}; + auto container = eof_bytecode(code, 4); + const auto subcontainer = eof_bytecode(OP_INVALID); + for (auto i = 0; i < 256; ++i) + container.container(subcontainer); + add_test_case(container, EOFValidationError::success); +} + +TEST_F(eof_validation, EOF1_embedded_container_invalid) +{ + // Truncated container header + add_test_case("EF0001 010004 0200010006 03", EOFValidationError::incomplete_section_number); + add_test_case("EF0001 010004 0200010006 0300", EOFValidationError::incomplete_section_number); + add_test_case( + "EF0001 010004 0200010006 030001", EOFValidationError::section_headers_not_terminated); + add_test_case("EF0001 010004 0200010006 03000100", EOFValidationError::incomplete_section_size); + add_test_case( + "EF0001 010004 0200010006 0300010014", EOFValidationError::section_headers_not_terminated); + + // Zero container sections + add_test_case("EF0001 010004 0200010006 030000 040000 00 00800001 60005D000000", + EOFValidationError::zero_section_size); + + // Container section with 0 size + add_test_case("EF0001 010004 0200010006 0300010000 040000 00 00800001 60005D000000", + EOFValidationError::zero_section_size); + + // Container body missing + add_test_case("EF0001 010004 0200010006 0300010014 040000 00 00800001 60005D000000", + EOFValidationError::invalid_section_bodies_size); + + // Too many container sections + auto code = eof_bytecode(rjumpi(0, 0) + OP_STOP, 1); + for (auto i = 0; i < 257; ++i) + code = code.container(OP_STOP); + add_test_case(code, EOFValidationError::too_many_container_sections); +} +TEST_F(eof_validation, EOF1_eofcreate_valid) +{ + // initcontainer_index = 0 + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode( + eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(0xff) + OP_POP + OP_STOP, 4) + .container(embedded), + EOFValidationError::success); + + // initcontainer_index = 0, 1 + add_test_case( + eof_bytecode(eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(0xff) + OP_POP + + eofcreate().container(1).input(0, OP_CALLDATASIZE).salt(0xfe) + OP_POP + + OP_STOP, + 4) + .container(embedded) + .container(embedded), + EOFValidationError::success); + + // initcontainer_index 0..255 + bytecode code; + for (auto i = 0; i < 256; ++i) + code += eofcreate().container(static_cast(i)).input(0, OP_CALLDATASIZE) + OP_POP; + code += bytecode{OP_STOP}; + auto cont = eof_bytecode(code, 4); + for (auto i = 0; i < 256; ++i) + cont.container(embedded); + add_test_case(cont, EOFValidationError::success); +} + +TEST_F(eof_validation, EOF1_eofcreate_invalid) +{ + // truncated immediate + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case(eof_bytecode(bytecode(0) + 0xff + 0 + 0 + OP_EOFCREATE, 4).container(embedded), + EOFValidationError::truncated_instruction); + + // last instruction + add_test_case( + eof_bytecode(bytecode(0) + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0}, 4).container(embedded), + EOFValidationError::no_terminating_instruction); + + // referring to non-existent container section + add_test_case( + eof_bytecode(bytecode(0) + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{1} + OP_POP + OP_STOP, 4) + .container(embedded), + EOFValidationError::invalid_container_section_index); + add_test_case( + eof_bytecode(bytecode(0) + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0xff} + OP_POP + OP_STOP, 4) + .container(embedded), + EOFValidationError::invalid_container_section_index); + + // referring to container with truncated data + const auto embedded_truncated_data = eof_bytecode(OP_INVALID).data("aabb"_hex, 3); + add_test_case( + eof_bytecode(bytecode(0) + 0xff + 0 + 0 + OP_EOFCREATE + Opcode{0} + OP_POP + OP_STOP, 4) + .container(embedded_truncated_data), + EOFValidationError::eofcreate_with_truncated_container); +} + +TEST_F(eof_validation, EOF1_returncontract_valid) +{ + // deploy_container_index = 0 + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case(eof_bytecode(returncontract(0, 0, 0), 2).container(embedded), + ContainerKind::initcode, EOFValidationError::success); + + // deploy_container_index = 0, 1 + add_test_case(eof_bytecode(rjumpi(6, 0) + returncontract(0, 0, 0) + returncontract(1, 0, 0), 2) + .container(embedded) + .container(embedded), + ContainerKind::initcode, EOFValidationError::success); + + // deploy_container_index = 0..255 + bytecode code; + for (auto i = 0; i < 256; ++i) + code += rjumpi(6, 0) + returncontract(static_cast(i), 0, 0); + code += revert(0, 0); + auto cont = eof_bytecode(code, 2); + for (auto i = 0; i < 256; ++i) + cont.container(embedded); + add_test_case(cont, ContainerKind::initcode, EOFValidationError::success); +} + +TEST_F(eof_validation, EOF1_returncontract_invalid) +{ + // truncated immediate + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case(eof_bytecode(bytecode(0) + 0 + OP_RETURNCONTRACT, 4).container(embedded), + ContainerKind::initcode, EOFValidationError::truncated_instruction); + + // referring to non-existent container section + add_test_case( + eof_bytecode(bytecode(0) + 0 + OP_RETURNCONTRACT + Opcode{1}, 4).container(embedded), + ContainerKind::initcode, EOFValidationError::invalid_container_section_index); + add_test_case( + eof_bytecode(bytecode(0) + 0 + OP_RETURNCONTRACT + Opcode{0xff}, 4).container(embedded), + ContainerKind::initcode, EOFValidationError::invalid_container_section_index); + + // Unreachable code after RETURNCONTRACT + add_test_case(eof_bytecode(bytecode(0) + 0 + OP_RETURNCONTRACT + Opcode{0} + revert(0, 0), 2) + .container(embedded), + ContainerKind::initcode, EOFValidationError::unreachable_instructions); +} + +TEST_F(eof_validation, EOF1_unreferenced_subcontainer_invalid) +{ + const auto embedded = eof_bytecode(bytecode{OP_INVALID}); + add_test_case( + eof_bytecode(OP_STOP).container(embedded), EOFValidationError::unreferenced_subcontainer); +} + +TEST_F(eof_validation, EOF1_subcontainer_containing_unreachable_code_sections) +{ + const auto embedded_1 = eof_bytecode(OP_INVALID).code(OP_INVALID, 0, 0x80, 0); + add_test_case(eof_bytecode(eofcreate() + OP_STOP, 4).container(embedded_1), + EOFValidationError::unreachable_code_sections); + + const auto embedded_2 = eof_bytecode(jumpf(1)) + .code(jumpf(2), 0, 0x80, 0) + .code(OP_INVALID, 0, 0x80, 0) + .code(jumpf(4), 0, 0x80, 0) + .code(jumpf(3), 0, 0x80, 0); + add_test_case(eof_bytecode(eofcreate() + OP_STOP, 4).container(embedded_2), + EOFValidationError::unreachable_code_sections); +} + +TEST_F(eof_validation, max_nested_containers_eofcreate) +{ + bytecode code{}; + bytecode nextcode = eof_bytecode(OP_INVALID); + while (nextcode.size() <= MAX_INITCODE_SIZE) { - auto code = eof1_bytecode(rjumpv({-4}, 0) + OP_RETF, 1); + code = nextcode; + nextcode = eof_bytecode(4 * push0() + OP_EOFCREATE + Opcode{0} + OP_INVALID, 4) + .container(nextcode); + } + add_test_case(code, EOFValidationError::success); +} - EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); +TEST_F(eof_validation, max_nested_containers_eofcreate_returncontract) +{ + bytecode code{}; + bytecode nextcode = eof_bytecode(OP_INVALID); + while (nextcode.size() <= MAX_INITCODE_SIZE) + { + code = nextcode; + + const bytecode initcode = + eof_bytecode(push0() + push0() + OP_RETURNCONTRACT + Opcode{0}, 2).container(nextcode); + if (initcode.size() >= std::numeric_limits::max()) + break; + nextcode = eof_bytecode(4 * push0() + OP_EOFCREATE + Opcode{0} + OP_INVALID, 4) + .container(initcode); } + add_test_case(code, EOFValidationError::success); +} + +// Summary of validity of combinations of referencing instructions with instructions inside +// referenced containers. +// Rows are instructions referencing subcontainers or rules for top-level container. +// Columns are instructions inside referenced subcontainer. +// +// | | STOP | RETURN | REVERT | RETURNCONTRACT | +// | ---------------------------- | ------ | ------ | ------ | -------------- | +// | top-level initcode | - | - | + | + | +// | EOFCREATE | - | - | + | + | +// | top-level runtime | + | + | + | - | +// | RETURNCONTRACT | + | + | + | - | +// | EOFCREATE and RETURNCONTRACT | - | - | + | - | + +TEST_F(eof_validation, initcode_container_stop) +{ + const auto initcode = bytecode{OP_STOP}; + const auto initcontainer = eof_bytecode(initcode, 0); + + add_test_case( + initcontainer, ContainerKind::initcode, EOFValidationError::incompatible_container_kind); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::incompatible_container_kind); } -TEST(eof_validation, callf_invalid_code_section_index) +TEST_F(eof_validation, initcode_container_return) { - EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 b0000100"), - EOFValidationError::invalid_code_section_index); + const auto initcode = ret(0, 0); + const auto initcontainer = eof_bytecode(initcode, 2); + + add_test_case( + initcontainer, ContainerKind::initcode, EOFValidationError::incompatible_container_kind); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::incompatible_container_kind); } -TEST(eof_validation, incomplete_section_size) +TEST_F(eof_validation, initcode_container_revert) { - EXPECT_EQ( - validate_eof("ef0001 010100 02003f 0100"), EOFValidationError::incomplete_section_size); + const auto initcode = revert(0, 0); + const auto initcontainer = eof_bytecode(initcode, 2); + + add_test_case(initcontainer, ContainerKind::initcode, EOFValidationError::success); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::success); } -TEST(eof_validation, data_section_missing) +TEST_F(eof_validation, initcode_container_returncontract) { - EXPECT_EQ(validate_eof("ef0001 010004 0200010001 00 00000000 fe"), - EOFValidationError::data_section_missing); + const auto initcode = returncontract(0, 0, 0); + const auto initcontainer = eof_bytecode(initcode, 2).container(eof_bytecode(OP_INVALID)); + + add_test_case(initcontainer, ContainerKind::initcode, EOFValidationError::success); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::success); } -TEST(eof_validation, multiple_code_sections_headers) +TEST_F(eof_validation, runtime_container_stop) { - EXPECT_EQ(validate_eof("0xef0001 010008 020001 0004 020001 0005 030000 00 00040000 045c0000 " - "00405c00 00002e0005"), - EOFValidationError::data_section_missing); + const auto runtime_container = eof_bytecode(OP_STOP); + + add_test_case(runtime_container, ContainerKind::runtime, EOFValidationError::success); + + const auto initcontainer = + eof_bytecode(returncontract(0, 0, 0), 2).container(runtime_container); + + add_test_case(initcontainer, ContainerKind::initcode, EOFValidationError::success); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::success); +} + +TEST_F(eof_validation, runtime_container_return) +{ + const auto runtime_container = eof_bytecode(ret(0, 0), 2); + + add_test_case(runtime_container, ContainerKind::runtime, EOFValidationError::success); + + const auto initcontainer = + eof_bytecode(returncontract(0, 0, 0), 2).container(runtime_container); + + add_test_case(initcontainer, ContainerKind::initcode, EOFValidationError::success); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::success); +} + +TEST_F(eof_validation, runtime_container_revert) +{ + const auto runtime_container = eof_bytecode(revert(0, 0), 2); + + add_test_case(runtime_container, ContainerKind::runtime, EOFValidationError::success); + + const auto initcontainer = + eof_bytecode(returncontract(0, 0, 0), 2).container(runtime_container); + + add_test_case(initcontainer, ContainerKind::initcode, EOFValidationError::success); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::success); +} + +TEST_F(eof_validation, runtime_container_returncontract) +{ + const auto runtime_container = + eof_bytecode(returncontract(0, 0, 0), 2).container(eof_bytecode(OP_INVALID)); + + add_test_case( + runtime_container, ContainerKind::runtime, EOFValidationError::incompatible_container_kind); + + const auto initcontainer = + eof_bytecode(returncontract(0, 0, 0), 2).container(runtime_container); + + add_test_case( + initcontainer, ContainerKind::initcode, EOFValidationError::incompatible_container_kind); + + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::incompatible_container_kind); } -TEST(eof_validation, many_code_sections_1023) -{ - auto code = - "0xef0001010ffc0203ff0001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010300000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000"; - EXPECT_EQ(validate_eof(code), EOFValidationError::success); -} - -TEST(eof_validation, many_code_sections_1024) -{ - auto code = - "0xef00010110000204000001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001030000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000"; - EXPECT_EQ(validate_eof(code), EOFValidationError::success); -} - -TEST(eof_validation, too_many_code_sections) -{ - auto code = - "0xef00010110040204010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" - "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" - "010001000100010001000100010001000100010001000100010001000100010001000103000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000000000000000000000"; - EXPECT_EQ(validate_eof(code), EOFValidationError::too_many_code_sections); +TEST_F(eof_validation, eofcreate_stop_and_returncontract) +{ + const auto runtime_container = eof_bytecode(OP_INVALID); + const auto initcode = rjumpi(1, 0) + OP_STOP + returncontract(0, 0, 0); + const auto initcontainer = eof_bytecode(initcode, 2).container(runtime_container); + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::incompatible_container_kind); +} + +TEST_F(eof_validation, eofcreate_return_and_returncontract) +{ + const auto runtime_container = eof_bytecode(OP_INVALID); + const auto initcode = rjumpi(5, 0) + ret(0, 0) + returncontract(0, 0, 0); + const auto initcontainer = eof_bytecode(initcode, 2).container(runtime_container); + const auto factory_code = eofcreate() + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(initcontainer); + + add_test_case(factory_container, EOFValidationError::incompatible_container_kind); +} + +TEST_F(eof_validation, eofcreate_and_returncontract_targeting_same_container) +{ + const auto runtime_container = eof_bytecode(OP_INVALID); + const auto initcode = eofcreate() + returncontract(0, 0, 0); + const auto initcontainer = eof_bytecode(initcode, 4).container(runtime_container); + + add_test_case( + initcontainer, ContainerKind::initcode, EOFValidationError::ambiguous_container_kind); + + const auto initcode2 = eofcreate() + eofcreate().container(1) + returncontract(1, 0, 0); + const auto initcontainer2 = + eof_bytecode(initcode, 4).container(runtime_container).container(runtime_container); + + add_test_case( + initcontainer2, ContainerKind::initcode, EOFValidationError::ambiguous_container_kind); } diff --git a/test/unittests/eos_evm_test.cpp b/test/unittests/eos_evm_test.cpp index f51f5d72..84caab4f 100644 --- a/test/unittests/eos_evm_test.cpp +++ b/test/unittests/eos_evm_test.cpp @@ -12,6 +12,7 @@ using namespace evmc::literals; using evmone::test::evm; using namespace evmone; +using namespace evmone::test; evmc_revision evm_version_to_revision[] = { EVMC_ISTANBUL, // eos_evm_version=0 @@ -421,15 +422,15 @@ TEST_P(evm, storage_gas_refund_eos_evm) // | | | | | | | 150000 | // | 3 | - | 0| 0| 0| 0| 149997 | push8 0x00 // | 3 | - | 0| 0| 0| 0| 149994 | push1 0xda - // | 2900 | - | 2800| 0| 0| 17100| 147094 | sstore + // | 2900 | - | 2800| 0| 0| 20000| 147094 | sstore // | 3 | - | 0| 0| 0| 0| 147091 | push8 0x00 // | 3 | - | 0| 0| 0| 0| 147088 | push1 0xdb - // | 2900 | - | 2800| 0| 0| 17100| 144188 | sstore + // | 2900 | - | 2800| 0| 0| 20000| 144188 | sstore // | 3 | - | 0| 0| 0| 0| 144185 | push8 0x00 // | 3 | - | 0| 0| 0| 0| 144182 | push1 0xdb - // | 2900 | - | 2800| 0| 0| 17100| 141282 | sstore + // | 2900 | - | 2800| 0| 0| 20000| 141282 | sstore // ---------------------------------------------------------------------------------END - // 8400| 0| 51300| 141282 | + // 8400| 0| 60000| 141282 | execute(150000, code); const auto gas_left_ = 141282; @@ -439,7 +440,7 @@ TEST_P(evm, storage_gas_refund_eos_evm) EXPECT_EQ(result.gas_left, gas_left_); EXPECT_EQ(result.gas_refund, 0); EXPECT_EQ(result.storage_gas_consumed, 0); - EXPECT_EQ(result.storage_gas_refund, 51300); + EXPECT_EQ(result.storage_gas_refund, 60000); EXPECT_EQ(result.speculative_cpu_gas_consumed, 8400); const auto real_cpu_consumed = (gas_used_ - result.storage_gas_consumed) - result.speculative_cpu_gas_consumed; @@ -474,24 +475,24 @@ TEST_P(evm, speculative_cpu_gas_consumed_eos_evm) // | | | | | | | 150000 | // | 3 | - | 0| 0| 0| 0| 149997 | push8 0xff // | 3 | - | 0| 0| 0| 0| 149994 | push1 0xda - // | 20000 | - | 2800| 0| 17100| 0| 129994 | sstore - // | 3 | - | 0| 0| 0| 0| 129991 | push8 0xff - // | 3 | - | 0| 0| 0| 0| 129988 | push1 0xdb - // | 20000 | - | 2800| 0| 17100| 0| 109988 | sstore - // | 3 | - | 0| 0| 0| 0| 109985 | push8 0xff - // | 3 | - | 0| 0| 0| 0| 109982 | push1 0xdb - // | 20000 | - | 2800| 0| 17100| 0| 89982 | sstore + // | 22900 | - | 2800| 0| 20000| 0| 127094 | sstore + // | 3 | - | 0| 0| 0| 0| 127091 | push8 0xff + // | 3 | - | 0| 0| 0| 0| 127088 | push1 0xdb + // | 22900 | - | 2800| 0| 20000| 0| 104188 | sstore + // | 3 | - | 0| 0| 0| 0| 104185 | push8 0xff + // | 3 | - | 0| 0| 0| 0| 104182 | push1 0xdb + // | 22900 | - | 2800| 0| 20000| 0| 81282 | sstore // ---------------------------------------------------------------------------------END - // 8400| 51300| 89982 | + // 8400| 60000| 81282 | execute(150000, code); - const auto gas_left_ = 89982; + const auto gas_left_ = 81282; const auto gas_used_ = 150000 - gas_left_; EXPECT_GAS_USED(EVMC_SUCCESS, gas_used_); EXPECT_EQ(result.gas_left, gas_left_); EXPECT_EQ(result.gas_refund, 0); - EXPECT_EQ(result.storage_gas_consumed, 51300); + EXPECT_EQ(result.storage_gas_consumed, 60000); EXPECT_EQ(result.storage_gas_refund, 0); EXPECT_EQ(result.speculative_cpu_gas_consumed, 8400); diff --git a/test/unittests/evm_benchmark_test.cpp b/test/unittests/evm_benchmark_test.cpp index da0d2631..8582fcad 100644 --- a/test/unittests/evm_benchmark_test.cpp +++ b/test/unittests/evm_benchmark_test.cpp @@ -10,7 +10,7 @@ #include using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, grow_memory_with_mload) { diff --git a/test/unittests/evm_calls_test.cpp b/test/unittests/evm_calls_test.cpp index 679185e7..3c160154 100644 --- a/test/unittests/evm_calls_test.cpp +++ b/test/unittests/evm_calls_test.cpp @@ -7,7 +7,10 @@ #include "evm_fixture.hpp" using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; + +inline constexpr auto max_uint256 = + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32; TEST_P(evm, delegatecall) { @@ -624,9 +627,14 @@ TEST_P(evm, call_value) { const auto has_value_arg = (op == OP_CALL || op == OP_CALLCODE); const auto value_cost = has_value_arg ? 9000 : 0; - const auto expected_value = has_value_arg ? passed_value : - (op == OP_DELEGATECALL) ? origin_value : - 0; + const auto expected_value = [=] { + if (has_value_arg) + return passed_value; + else if (op == OP_DELEGATECALL) + return origin_value; + else + return 0; + }(); const auto code = 4 * push(0) + push(has_value_arg ? passed_value : 0) + push(recipient) + push(0) + op; @@ -652,72 +660,64 @@ TEST_P(evm, create_oog_after) TEST_P(evm, returndatasize_before_call) { - execute(bytecode{"3d60005360016000f3"}); - EXPECT_EQ(gas_used, 17); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 0); + execute(returndatasize() + ret_top()); + EXPECT_GAS_USED(EVMC_SUCCESS, 17); + EXPECT_OUTPUT_INT(0); } TEST_P(evm, returndatasize) { - uint8_t call_output[13]; - host.call_result.output_size = std::size(call_output); - host.call_result.output_data = std::begin(call_output); + const uint8_t call_output[13]{}; + host.call_result.output_data = std::data(call_output); - const auto code = - push(0) + 5 * OP_DUP1 + OP_DELEGATECALL + mstore8(0, OP_RETURNDATASIZE) + ret(0, 1); + const auto code = delegatecall(0) + returndatasize() + ret_top(); + + host.call_result.status_code = EVMC_SUCCESS; + host.call_result.output_size = std::size(call_output); execute(code); - EXPECT_EQ(gas_used, 735); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], std::size(call_output)); + EXPECT_GAS_USED(EVMC_SUCCESS, 735); + EXPECT_OUTPUT_INT(std::size(call_output)); - host.call_result.output_size = 1; host.call_result.status_code = EVMC_FAILURE; + host.call_result.output_size = 1; execute(code); - EXPECT_EQ(gas_used, 735); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); + EXPECT_GAS_USED(EVMC_SUCCESS, 735); + EXPECT_OUTPUT_INT(1); - host.call_result.output_size = 0; host.call_result.status_code = EVMC_INTERNAL_ERROR; + host.call_result.output_size = 0; execute(code); - EXPECT_EQ(gas_used, 735); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 0); + EXPECT_GAS_USED(EVMC_SUCCESS, 735); + EXPECT_OUTPUT_INT(0); } TEST_P(evm, returndatacopy) { - uint8_t call_output[32] = {1, 2, 3, 4, 5, 6, 7}; - host.call_result.output_size = std::size(call_output); - host.call_result.output_data = std::begin(call_output); + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + host.call_result.output_data = std::data(call_output.bytes); + host.call_result.output_size = std::size(call_output.bytes); - const bytecode code = "600080808060aa60fff4506020600060003e60206000f3"; + const auto code = delegatecall(0) + returndatacopy(0, 0, 32) + ret(0, 32); execute(code); - EXPECT_EQ(gas_used, 999); - ASSERT_EQ(result.output_size, 32); - EXPECT_EQ(result.output_data[0], 1); - EXPECT_EQ(result.output_data[1], 2); - EXPECT_EQ(result.output_data[2], 3); - EXPECT_EQ(result.output_data[6], 7); - EXPECT_EQ(result.output_data[7], 0); + EXPECT_GAS_USED(EVMC_SUCCESS, 742); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), call_output); } TEST_P(evm, returndatacopy_empty) { - const bytecode code = "600080808060aa60fff4600080803e60016000f3"; - execute(code); - EXPECT_EQ(gas_used, 994); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 0); + execute(delegatecall(0) + returndatacopy(0, 0, 0) + ret(0, 32)); + EXPECT_GAS_USED(EVMC_SUCCESS, 739); + EXPECT_OUTPUT_INT(0); } TEST_P(evm, returndatacopy_cost) { - auto call_output = uint8_t{}; - host.call_result.output_data = &call_output; - host.call_result.output_size = sizeof(call_output); - auto code = "60008080808080fa6001600060003e"; + const uint8_t call_output[1]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + const auto code = staticcall(0) + returndatacopy(0, 0, 1); execute(736, code); EXPECT_EQ(result.status_code, EVMC_SUCCESS); execute(735, code); @@ -726,17 +726,68 @@ TEST_P(evm, returndatacopy_cost) TEST_P(evm, returndatacopy_outofrange) { - auto call_output = uint8_t{}; - host.call_result.output_data = &call_output; - host.call_result.output_size = sizeof(call_output); - execute(735, "60008080808080fa6002600060003e"); + const uint8_t call_output[2]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(735, staticcall(0) + returndatacopy(0, 0, 3)); EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); - execute(735, "60008080808080fa6001600160003e"); + execute(735, staticcall(0) + returndatacopy(0, 1, 2)); EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); - execute(735, "60008080808080fa6000600260003e"); + execute(735, staticcall(0) + returndatacopy(0, 2, 1)); EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + + execute(735, staticcall(0) + returndatacopy(0, 3, 0)); + EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + + execute(735, staticcall(0) + returndatacopy(0, 1, 0)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + + execute(735, staticcall(0) + returndatacopy(0, 2, 0)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); +} + +TEST_P(evm, returndatacopy_outofrange_highbits) +{ + const uint8_t call_output[2]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + // Covers an incorrect cast of RETURNDATACOPY arg to `size_t` ignoring the high bits. + const auto highbits = + 0x1000000000000000000000000000000000000000000000000000000000000000_bytes32; + execute(735, staticcall(0) + returndatacopy(0, highbits, 0)); + EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); +} + +TEST_P(evm, returndataload_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + execute(staticcall(0) + returndataload(0)); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, extcall_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + execute(extcall(0)); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, extdelegatecall_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + execute(extdelegatecall(0)); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, extstaticcall_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + execute(extstaticcall(0)); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); } TEST_P(evm, call_gas_refund_propagation) diff --git a/test/unittests/evm_control_flow_test.cpp b/test/unittests/evm_control_flow_test.cpp index c946c266..6caff1ce 100644 --- a/test/unittests/evm_control_flow_test.cpp +++ b/test/unittests/evm_control_flow_test.cpp @@ -4,7 +4,7 @@ #include "evm_fixture.hpp" -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, jump) { diff --git a/test/unittests/evm_eip2929_test.cpp b/test/unittests/evm_eip2929_test.cpp index e9f4e324..00f32b07 100644 --- a/test/unittests/evm_eip2929_test.cpp +++ b/test/unittests/evm_eip2929_test.cpp @@ -8,7 +8,7 @@ #include "evm_fixture.hpp" using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, eip2929_case1) { diff --git a/test/unittests/evm_eip3198_basefee_test.cpp b/test/unittests/evm_eip3198_basefee_test.cpp index 74607b48..8bc1586f 100644 --- a/test/unittests/evm_eip3198_basefee_test.cpp +++ b/test/unittests/evm_eip3198_basefee_test.cpp @@ -7,8 +7,7 @@ #include "evm_fixture.hpp" -using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, basefee_pre_london) { diff --git a/test/unittests/evm_eip3855_push0_test.cpp b/test/unittests/evm_eip3855_push0_test.cpp index 77caac31..392395ef 100644 --- a/test/unittests/evm_eip3855_push0_test.cpp +++ b/test/unittests/evm_eip3855_push0_test.cpp @@ -8,7 +8,7 @@ #include "evm_fixture.hpp" using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, push0_pre_shanghai) { diff --git a/test/unittests/evm_eip3860_initcode_test.cpp b/test/unittests/evm_eip3860_initcode_test.cpp index 72f756e9..34943ca1 100644 --- a/test/unittests/evm_eip3860_initcode_test.cpp +++ b/test/unittests/evm_eip3860_initcode_test.cpp @@ -8,7 +8,7 @@ #include "evm_fixture.hpp" using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; inline constexpr size_t initcode_size_limit = 0xc000; diff --git a/test/unittests/evm_eip4844_blobhash_test.cpp b/test/unittests/evm_eip4844_blobhash_test.cpp new file mode 100644 index 00000000..b872f1b9 --- /dev/null +++ b/test/unittests/evm_eip4844_blobhash_test.cpp @@ -0,0 +1,108 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains EVM unit tests for the BLOBHASH instruction from EIP-4844 +/// https://eips.ethereum.org/EIPS/eip-4844 + +#include "evm_fixture.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_P(evm, blobhash_undefined) +{ + rev = EVMC_SHANGHAI; + execute(blobhash(0)); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, blobhash_empty) +{ + rev = EVMC_CANCUN; + execute(blobhash(0) + ret_top()); + EXPECT_OUTPUT_INT(0); + + execute(blobhash(1) + ret_top()); + EXPECT_OUTPUT_INT(0); + + execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) + + ret_top()); + EXPECT_OUTPUT_INT(0); +} + +TEST_P(evm, blobhash_one) +{ + rev = EVMC_CANCUN; + + const std::array blob_hashes{ + 0x01feeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed_bytes32}; + + host.tx_context.blob_hashes = blob_hashes.data(); + host.tx_context.blob_hashes_count = blob_hashes.size(); + + execute(blobhash(0) + ret_top()); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(output, blob_hashes[0]); + + execute(blobhash(1) + ret_top()); + EXPECT_OUTPUT_INT(0); + + execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) + + ret_top()); + EXPECT_OUTPUT_INT(0); +} + +TEST_P(evm, blobhash_two) +{ + rev = EVMC_CANCUN; + + const std::array blob_hashes{ + 0x0100000000000000000000000000000000000000000000000000000000000001_bytes32, + 0x0100000000000000000000000000000000000000000000000000000000000002_bytes32}; + + host.tx_context.blob_hashes = blob_hashes.data(); + host.tx_context.blob_hashes_count = blob_hashes.size(); + + for (size_t i = 0; i < blob_hashes.size(); ++i) + { + execute(blobhash(i) + ret_top()); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(output, blob_hashes[i]); + } + + execute(blobhash(blob_hashes.size()) + ret_top()); + EXPECT_OUTPUT_INT(0); + + execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) + + ret_top()); + EXPECT_OUTPUT_INT(0); +} + +TEST_P(evm, blobhash_invalid_hash_version) +{ + rev = EVMC_CANCUN; + + // The BLOBHASH instruction does not care about the hash version, + // it will return whatever is in the array. + const std::array blob_hashes{ + 0x0000000000000000000000000000000000000000000000000000000000000000_bytes32, + 0x0200000000000000000000000000000000000000000000000000000000000000_bytes32}; + + host.tx_context.blob_hashes = blob_hashes.data(); + host.tx_context.blob_hashes_count = blob_hashes.size(); + + for (size_t i = 0; i < blob_hashes.size(); ++i) + { + execute(blobhash(i) + ret_top()); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(output, blob_hashes[i]); + } + + execute(blobhash(blob_hashes.size()) + ret_top()); + EXPECT_OUTPUT_INT(0); + + execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) + + ret_top()); + EXPECT_OUTPUT_INT(0); +} diff --git a/test/unittests/evm_eip663_dupn_swapn_test.cpp b/test/unittests/evm_eip663_dupn_swapn_test.cpp index acc77911..153acd16 100644 --- a/test/unittests/evm_eip663_dupn_swapn_test.cpp +++ b/test/unittests/evm_eip663_dupn_swapn_test.cpp @@ -3,12 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" -#include -using namespace evmc::literals; -using namespace evmone; -using namespace intx; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, dupn) { @@ -16,26 +12,23 @@ TEST_P(evm, dupn) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto pushes = bytecode{}; for (uint64_t i = 1; i <= 20; ++i) pushes += push(i); - execute(pushes + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_DUPN + "02" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "02" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(18); - execute(pushes + OP_DUPN + "13" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "13" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - - execute(pushes + OP_DUPN + "14" + ret_top()); - EXPECT_STATUS(EVMC_STACK_UNDERFLOW); } TEST_P(evm, swapn) @@ -44,38 +37,35 @@ TEST_P(evm, swapn) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto pushes = bytecode{}; for (uint64_t i = 1; i <= 20; ++i) pushes += push(i); - execute(pushes + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(19); - execute(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_SWAPN + "01" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "01" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(18); - execute(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_SWAPN + "12" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "12" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - - execute(pushes + OP_SWAPN + "13" + ret_top()); - EXPECT_STATUS(EVMC_STACK_UNDERFLOW); } TEST_P(evm, dupn_full_stack) @@ -84,25 +74,22 @@ TEST_P(evm, dupn_full_stack) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto full_stack_code = bytecode{}; - for (uint64_t i = 1023; i >= 1; --i) + for (uint64_t i = 1022; i >= 1; --i) full_stack_code += push(i); - execute(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(257); - execute(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(256); - - execute(full_stack_code + OP_DUPN + "fe" + OP_DUPN + "ff"); - EXPECT_STATUS(EVMC_STACK_OVERFLOW); } TEST_P(evm, swapn_full_stack) @@ -111,24 +98,24 @@ TEST_P(evm, swapn_full_stack) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto full_stack_code = bytecode{}; - for (uint64_t i = 1024; i >= 1; --i) + for (uint64_t i = 1023; i >= 1; --i) full_stack_code += push(i); - execute(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(3); - execute(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(255 + 3); - execute(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(254 + 3); - execute(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN); + execute(eof_bytecode(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN, 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(result.output_size, 255 + 2); } @@ -139,24 +126,24 @@ TEST_P(evm, dupn_dup_consistency) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto pushes = bytecode{}; for (uint64_t i = 32; i >= 1; --i) pushes += push(i); - execute(pushes + OP_DUP1 + ret_top()); + execute(eof_bytecode(pushes + OP_DUP1 + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_DUP16 + ret_top()); + execute(eof_bytecode(pushes + OP_DUP16 + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(16); - execute(pushes + OP_DUPN + "0f" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "0f" + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(16); } @@ -167,24 +154,35 @@ TEST_P(evm, swapn_swap_consistency) if (evm::is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; auto pushes = bytecode{}; for (uint64_t i = 32; i >= 1; --i) pushes += push(i); - execute(pushes + OP_SWAP1 + ret_top()); + execute(eof_bytecode(pushes + OP_SWAP1 + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(pushes + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(pushes + OP_SWAP16 + ret_top()); + execute(eof_bytecode(pushes + OP_SWAP16 + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(17); - execute(pushes + OP_SWAPN + "0f" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "0f" + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(17); } + +TEST_P(evm, dupn_swapn_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + + execute(push(1) + push(2) + OP_SWAPN + "00"); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); + + execute(push(1) + OP_DUPN + "00"); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} diff --git a/test/unittests/evm_eip663_exchange_test.cpp b/test/unittests/evm_eip663_exchange_test.cpp new file mode 100644 index 00000000..292acd71 --- /dev/null +++ b/test/unittests/evm_eip663_exchange_test.cpp @@ -0,0 +1,111 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evm_fixture.hpp" + +using namespace evmone::test; + + +TEST_P(evm, exchange) +{ + // EXCHANGE is not implemented in Advanced. + if (evm::is_advanced()) + return; + + rev = EVMC_PRAGUE; + + auto pushes = bytecode{}; + for (uint64_t i = 1; i <= 20; ++i) + pushes += push(i); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "00" + ret_top(), 21)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(20); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "00" + OP_DUPN + "01" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(18); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "00" + OP_DUPN + "02" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(19); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "01" + ret_top(), 21)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(20); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "01" + OP_DUPN + "01" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(17); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "01" + OP_DUPN + "03" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(19); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "10" + ret_top(), 21)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(20); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "10" + OP_DUPN + "02" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(17); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "10" + OP_DUPN + "03" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(18); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "f2" + ret_top(), 21)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(20); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "f2" + OP_DUPN + "10" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(1); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "f2" + OP_DUPN + "13" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(4); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "0f" + ret_top(), 21)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(20); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "0f" + OP_DUPN + "01" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(3); + + execute(eof_bytecode(pushes + OP_EXCHANGE + "0f" + OP_DUPN + "11" + ret_top(), 22)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(19); +} + +TEST_P(evm, exchange_deep_stack) +{ + // EXCHANGE is not implemented in Advanced. + if (evm::is_advanced()) + return; + + rev = EVMC_PRAGUE; + auto full_stack_code = bytecode{}; + for (uint64_t i = 255; i >= 1; --i) + full_stack_code += push(i); + + execute(eof_bytecode(full_stack_code + OP_EXCHANGE + "ff" + ret_top(), 256)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(1); + execute(eof_bytecode(full_stack_code + OP_EXCHANGE + "ff" + OP_DUPN + "10" + ret_top(), 257)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(33); + execute(eof_bytecode(full_stack_code + OP_EXCHANGE + "ff" + OP_DUPN + "20" + ret_top(), 257)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(17); +} + +TEST_P(evm, exchange_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + + execute(push(1) + push(2) + push(3) + OP_EXCHANGE + "00"); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} diff --git a/test/unittests/evm_eip7516_blobbasefee_test.cpp b/test/unittests/evm_eip7516_blobbasefee_test.cpp new file mode 100644 index 00000000..2059d59d --- /dev/null +++ b/test/unittests/evm_eip7516_blobbasefee_test.cpp @@ -0,0 +1,44 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains EVM unit tests for EIP-7516: "BLOBBASEFEE opcode" +/// https://eips.ethereum.org/EIPS/eip-7516 + +#include "evm_fixture.hpp" + +using namespace evmc::literals; +using namespace intx::literals; +using namespace evmone::test; + +TEST_P(evm, blobbasefee_pre_cancun) +{ + rev = EVMC_SHANGHAI; + const auto code = bytecode{OP_BLOBBASEFEE}; + + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, blobbasefee_1) +{ + rev = EVMC_CANCUN; + host.tx_context.blob_base_fee = 0x01_bytes32; + + execute(bytecode{} + OP_BLOBBASEFEE); + EXPECT_GAS_USED(EVMC_SUCCESS, 2); + + execute(bytecode{} + OP_BLOBBASEFEE + ret_top()); + EXPECT_GAS_USED(EVMC_SUCCESS, 17); + EXPECT_OUTPUT_INT(1); +} + +TEST_P(evm, blobbasefee_dede) +{ + rev = EVMC_CANCUN; + host.tx_context.blob_base_fee = + 0x8ededededededededededededededededededededededededededededededed1_bytes32; + + execute(bytecode{} + OP_BLOBBASEFEE + ret_top()); + EXPECT_OUTPUT_INT(0x8ededededededededededededededededededededededededededededededed1_u256); +} diff --git a/test/unittests/evm_eof_calls_test.cpp b/test/unittests/evm_eof_calls_test.cpp new file mode 100644 index 00000000..3800e326 --- /dev/null +++ b/test/unittests/evm_eof_calls_test.cpp @@ -0,0 +1,671 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evm_fixture.hpp" +#include + +using namespace evmc::literals; +using namespace evmone::test; + +inline constexpr auto max_uint256 = + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32; + +// Controlled amount of gas crossing the minimum callee/retained gas thresholds. +inline constexpr auto safe_call_gas = 400000; + +TEST_P(evm, extdelegatecall) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + host.access_account(callee); + host.accounts[callee].code = "EF00"_hex; + + auto code = eof_bytecode(mstore(0, push(1) + push0() + OP_SUB) + + extdelegatecall(callee).input(0x2, 0x3) + + returndatacopy(0x4, 0x0, 0x5) + ret(0, 8), + 4); + + auto call_output = bytes{0xa, 0xb, 0xc, 0xd, 0xe}; + host.call_result.output_data = call_output.data(); + host.call_result.output_size = call_output.size(); + host.call_result.gas_left = 1; + + msg.value.bytes[17] = 0xfe; + + execute(safe_call_gas, code); + + auto gas_before_call = 3 + 2 + 3 + 3 + 6 + 3 * 3 + 100; + auto gas_left = safe_call_gas - gas_before_call; + ASSERT_EQ(host.recorded_calls.size(), 1); + const auto& call_msg = host.recorded_calls.back(); + EXPECT_EQ(call_msg.gas, gas_left - gas_left / 64); + EXPECT_EQ(call_msg.input_size, 3); + EXPECT_EQ(call_msg.value.bytes[17], 0xfe); + + ASSERT_EQ(result.output_size, 8); + EXPECT_EQ(output, (bytes{0xff, 0xff, 0xff, 0xff, 0xa, 0xb, 0xc, 0xd})); + + EXPECT_GAS_USED(EVMC_SUCCESS, + gas_before_call + call_msg.gas - host.call_result.gas_left + 3 + 3 + 3 + 3 + 3 + 3 + 3); +} + +TEST_P(evm, extdelegatecall_oog_depth_limit) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + host.access_account(callee); + host.accounts[callee].code = "EF00"_hex; + + msg.depth = 1024; + const auto code = eof_bytecode(extdelegatecall(callee) + ret_top(), 3); + + execute(safe_call_gas, code); + EXPECT_EQ(host.recorded_calls.size(), 0); + auto expected_gas_used = 3 * 3 + 100 + 3 + 3 + 3 + 3 + 3; + EXPECT_GAS_USED(EVMC_SUCCESS, expected_gas_used); + EXPECT_OUTPUT_INT(1); + + execute(expected_gas_used, code); + EXPECT_STATUS(EVMC_SUCCESS); // MIN_CALLEE_GAS failure is light failure as well. + EXPECT_OUTPUT_INT(1); +} + +TEST_P(evm, extcall_failing_with_value) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + host.access_account(callee); + host.accounts[callee] = {}; + + const auto code = eof_bytecode(extcall(callee).input(0x0, 0xff).value(0x1) + OP_STOP, 4); + + // Fails on balance check. + execute(safe_call_gas, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 4 * 3 + 100 + 8 * 3 + 9000); + EXPECT_EQ(host.recorded_calls.size(), 0); // There was no call(). + + // Fails on value transfer additional cost - minimum gas limit that triggers this + execute(4 * 3 + 100 + 8 * 3, code); + EXPECT_STATUS(EVMC_OUT_OF_GAS); + EXPECT_EQ(host.recorded_calls.size(), 0); // There was no call(). + + // Fails on value transfer additional cost - maximum gas limit that triggers this + execute(4 * 3 + 100 + 8 * 3 + 9000 - 1, code); + EXPECT_STATUS(EVMC_OUT_OF_GAS); + EXPECT_EQ(host.recorded_calls.size(), 0); // There was no call(). +} + +TEST_P(evm, extcall_with_value_depth_limit) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + + constexpr auto call_dst = 0x00000000000000000000000000000000000000aa_address; + host.accounts[call_dst] = {}; + + msg.depth = 1024; + execute(eof_bytecode(extcall(call_dst).input(0x0, 0xff).value(0x1) + OP_STOP, 4)); + + EXPECT_EQ(gas_used, 4 * 3 + 2600 + 8 * 3 + 9000); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(host.recorded_calls.size(), 0); +} + +TEST_P(evm, extcall_depth_limit) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + host.access_account(callee); + host.accounts[callee].code = "EF00"_hex; + msg.depth = 1024; + + for (auto op : {OP_EXTCALL, OP_EXTDELEGATECALL, OP_EXTSTATICCALL}) + { + const auto code = eof_bytecode(push(callee) + 3 * push0() + op + ret_top(), 4); + execute(code); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(host.recorded_calls.size(), 0); + EXPECT_OUTPUT_INT(1); + } +} + +TEST_P(evm, extcall_value_zero_to_nonexistent_account) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + host.call_result.gas_left = 1000; + + const auto code = eof_bytecode(extcall(0xaa).input(0, 0x40) + OP_STOP, 4); + + execute(safe_call_gas, code); + auto gas_before_call = 4 * 3 + 2 * 3 + 2600; + auto gas_left = safe_call_gas - gas_before_call; + ASSERT_EQ(host.recorded_calls.size(), 1); + const auto& call_msg = host.recorded_calls.back(); + EXPECT_EQ(call_msg.kind, EVMC_CALL); + EXPECT_EQ(call_msg.depth, 1); + EXPECT_EQ(call_msg.gas, gas_left - gas_left / 64); + EXPECT_EQ(call_msg.input_size, 64); + EXPECT_EQ(call_msg.recipient, 0x00000000000000000000000000000000000000aa_address); + EXPECT_EQ(call_msg.value.bytes[31], 0); + + EXPECT_GAS_USED(EVMC_SUCCESS, gas_before_call + call_msg.gas - host.call_result.gas_left); +} + +TEST_P(evm, extcall_new_account_creation_cost) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + constexpr auto call_dst = 0x00000000000000000000000000000000000000ad_address; + constexpr auto msg_dst = 0x0000000000000000000000000000000000000003_address; + const auto code = + eof_bytecode(extcall(call_dst).value(calldataload(0)).input(0, 0) + ret_top(), 4); + + msg.recipient = msg_dst; + + rev = EVMC_PRAGUE; + { + auto gas_before_call = 3 * 3 + 3 + 3 + 2600; + auto gas_left = safe_call_gas - gas_before_call; + + host.accounts[msg.recipient].set_balance(0); + execute(safe_call_gas, code, "00"_hex); + EXPECT_OUTPUT_INT(0); + ASSERT_EQ(host.recorded_calls.size(), 1); + auto& call_msg = host.recorded_calls.back(); + EXPECT_EQ(call_msg.recipient, call_dst); + EXPECT_EQ(call_msg.gas, gas_left - gas_left / 64); + EXPECT_EQ(call_msg.sender, msg_dst); + EXPECT_EQ(call_msg.value.bytes[31], 0); + EXPECT_EQ(call_msg.input_size, 0); + EXPECT_GAS_USED(EVMC_SUCCESS, gas_before_call + call_msg.gas + 3 + 3 + 3 + 3 + 3); + ASSERT_EQ(host.recorded_account_accesses.size(), 4); + EXPECT_EQ(host.recorded_account_accesses[0], 0x00_address); // EIP-2929 tweak + EXPECT_EQ(host.recorded_account_accesses[1], msg.recipient); // EIP-2929 tweak + EXPECT_EQ(host.recorded_account_accesses[2], call_dst); // ? + EXPECT_EQ(host.recorded_account_accesses[3], call_dst); // Call. + host.recorded_account_accesses.clear(); + host.recorded_calls.clear(); + } + { + auto gas_before_call = 3 * 3 + 3 + 3 + 2600 + 25000 + 9000; + auto gas_left = safe_call_gas - gas_before_call; + + host.accounts[msg.recipient].set_balance(1); + execute(safe_call_gas, code, + "0000000000000000000000000000000000000000000000000000000000000001"_hex); + EXPECT_OUTPUT_INT(0); + ASSERT_EQ(host.recorded_calls.size(), 1); + auto& call_msg = host.recorded_calls.back(); + EXPECT_EQ(call_msg.recipient, call_dst); + EXPECT_EQ(call_msg.gas, gas_left - gas_left / 64); + EXPECT_EQ(call_msg.sender, msg_dst); + EXPECT_EQ(call_msg.value.bytes[31], 1); + EXPECT_EQ(call_msg.input_size, 0); + EXPECT_GAS_USED(EVMC_SUCCESS, gas_before_call + call_msg.gas + 3 + 3 + 3 + 3 + 3); + ASSERT_EQ(host.recorded_account_accesses.size(), 6); + EXPECT_EQ(host.recorded_account_accesses[0], 0x00_address); // EIP-2929 tweak + EXPECT_EQ(host.recorded_account_accesses[1], msg.recipient); // EIP-2929 tweak + EXPECT_EQ(host.recorded_account_accesses[2], call_dst); // ? + EXPECT_EQ(host.recorded_account_accesses[3], call_dst); // Account exist?. + EXPECT_EQ(host.recorded_account_accesses[4], msg.recipient); // Balance. + EXPECT_EQ(host.recorded_account_accesses[5], call_dst); // Call. + host.recorded_account_accesses.clear(); + host.recorded_calls.clear(); + } +} + +TEST_P(evm, extcall_oog_after_balance_check) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + // Create the call destination account. + host.accounts[0x0000000000000000000000000000000000000000_address] = {}; + auto code = eof_bytecode(extcall(0).value(1) + OP_POP + OP_STOP, 4); + execute(9112, code); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, extcall_oog_after_depth_check) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + // Create the call recipient account. + host.accounts[0x0000000000000000000000000000000000000000_address] = {}; + msg.depth = 1024; + + auto code = eof_bytecode(extcall(0).value(1) + OP_POP + OP_STOP, 4); + execute(9112, code); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, returndataload) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + host.call_result.output_data = std::data(call_output.bytes); + host.call_result.output_size = std::size(call_output.bytes); + + const auto code = eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3); + + execute(code); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), call_output); +} + +TEST_P(evm, returndataload_cost) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[32]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + host.call_result.gas_left = 0; + + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + OP_STOP, 3)); + const auto gas_with = gas_used; + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + execute(eof_bytecode(extstaticcall(0) + push(0) + OP_STOP, 3)); + EXPECT_GAS_USED(EVMC_SUCCESS, gas_with - 3); +} + +TEST_P(evm, returndataload_oog) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[32]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + host.call_result.gas_left = 0; + + constexpr auto retained_gas = 5000; + constexpr auto gas = 3 * 3 + 100 + retained_gas * 64; + // Uses OP_JUMPDEST to burn gas retained by the caller. + execute(gas, eof_bytecode(extstaticcall(0) + (retained_gas - 3 - 3) * OP_JUMPDEST + + returndataload(0) + OP_STOP, + 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + + execute(gas, eof_bytecode(extstaticcall(0) + (retained_gas - 3 - 2) * OP_JUMPDEST + + returndataload(0) + OP_STOP, + 3)); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, returndataload_outofrange) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + { + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 31 bytes + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); + } + { + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 32 bytes + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(31) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaa00000000000000000000000000000000000000000000000000000000000000"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(32) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + } + { + // 34 bytes + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(eof_bytecode(extstaticcall(0) + returndataload(3) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(2) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + } + { + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 64 bytes + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(eof_bytecode(extstaticcall(0) + returndataload(33) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); + + + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(31) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(32) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + } +} + +TEST_P(evm, returndataload_empty) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); + + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); + + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); +} + +TEST_P(evm, returndataload_outofrange_highbits) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[34]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + // Covers an incorrect cast of RETURNDATALOAD arg to `size_t` ignoring the high bits. + const auto highbits = + 0x1000000000000000000000000000000000000000000000000000000000000000_bytes32; + execute(eof_bytecode(extstaticcall(0) + returndataload(highbits) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); +} + +TEST_P(evm, extcall_gas_refund_aggregation_different_calls) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + host.access_account(callee); + host.accounts[callee].code = "EF00"_hex; + host.accounts[msg.recipient].set_balance(1); + host.call_result.status_code = EVMC_SUCCESS; + host.call_result.gas_refund = 1; + + const auto code = eof_bytecode( + extcall(callee) + extdelegatecall(callee) + extstaticcall(callee) + OP_STOP, 5); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(result.gas_refund, 3); +} + +TEST_P(evm, extcall_gas_refund_aggregation_same_calls) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + constexpr auto callee = 0xaa_address; + host.access_account(callee); + host.accounts[callee].code = "EF00"_hex; + host.accounts[msg.recipient].set_balance(2); + host.call_result.status_code = EVMC_SUCCESS; + host.call_result.gas_refund = 1; + + execute(eof_bytecode(2 * extcall(callee).value(1).input(1, 1) + OP_STOP, 5)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(result.gas_refund, 2); + + execute(eof_bytecode(2 * extdelegatecall(callee).input(1, 1) + OP_STOP, 4)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(result.gas_refund, 2); + + execute(eof_bytecode(2 * extstaticcall(callee).input(1, 1) + OP_STOP, 4)); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(result.gas_refund, 2); +} + +TEST_P(evm, eof_returndatacopy) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + host.call_result.output_data = std::data(call_output.bytes); + host.call_result.output_size = std::size(call_output.bytes); + + const auto code = eof_bytecode(extcall(0) + returndatacopy(0, 0, 32) + ret(0, 32), 4); + execute(safe_call_gas, code); + + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), call_output); +} + +TEST_P(evm, eof_returndatacopy_empty) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + execute(eof_bytecode(extcall(0) + returndatacopy(0, 0, 0) + ret(0, 32), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_OUTPUT_INT(0); +} + +TEST_P(evm, eof_returndatacopy_oog) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[1]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + constexpr auto retained_gas = 5000; + constexpr auto gas = 3 * 3 + 100 + retained_gas * 64; + // Uses OP_JUMPDEST to burn gas retained by the caller. + execute(gas, + eof_bytecode(extstaticcall(0) + (retained_gas - 3 * 3 - 3 - 1 * 3 - 1 * 3) * OP_JUMPDEST + + returndatacopy(0, 0, 1) + OP_STOP, + 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + + execute(gas, eof_bytecode(extstaticcall(0) + + (retained_gas - 3 * 3 - 3 - 1 * 3 - 1 * 3 + 1) * OP_JUMPDEST + + returndatacopy(0, 0, 1) + OP_STOP, + 4)); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, eof_returndatacopy_cost) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[1]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + host.call_result.gas_left = 0; + + execute(eof_bytecode(extstaticcall(0) + returndatacopy(0, 0, 1) + OP_STOP, 4)); + const auto gas_with = gas_used; + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + execute(eof_bytecode(extstaticcall(0) + 3 * push(0) + OP_STOP, 4)); + EXPECT_GAS_USED(EVMC_SUCCESS, gas_with - 3 - 1 * 3 - 1 * 3); +} + +TEST_P(evm, eof_returndatacopy_outofrange) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[2]{0xab, 0xcd}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 0, 3) + ret(0, 3), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0xabcd00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 1, 2) + ret(0, 2), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0xcd00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 2, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 3, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); + + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, 3, 0) + ret(0, OP_MSIZE), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x"_hex); +} + +TEST_P(evm, eof_returndatacopy_outofrange_highbits) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[2]{0xab, 0xcd}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + const auto highbits = + 0x1000000000000000000000000000000000000000000000000000000000000000_bytes32; + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, highbits, 0) + ret(0, OP_MSIZE), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x"_hex); + + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, highbits, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); +} diff --git a/test/unittests/evm_eof_function_test.cpp b/test/unittests/evm_eof_function_test.cpp index d02b696e..be59930a 100644 --- a/test/unittests/evm_eof_function_test.cpp +++ b/test/unittests/evm_eof_function_test.cpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" -#include "evmone/eof.hpp" +#include -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, eof_function_example1) { @@ -13,14 +13,9 @@ TEST_P(evm, eof_function_example1) if (is_advanced()) return; - rev = EVMC_CANCUN; - const auto code = - "EF00 01 010008 020002 000f 0002 030000 00" - "00000002 02010002" - "6001 6008 b00001 " + - ret_top() + "03b1"; - - ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{}); + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode(push(1) + push(8) + OP_CALLF + "0001" + ret_top(), 2) + .code(bytecode{OP_SUB} + OP_RETF, 2, 1, 2); execute(code); EXPECT_GAS_USED(EVMC_SUCCESS, 32); @@ -33,15 +28,13 @@ TEST_P(evm, eof_function_example2) if (is_advanced()) return; - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; const auto code = - "ef0001 01000c 020003 003b 0017 001d 030000 00 00000004 01010003 01010004" - "60043560003560e01c63c766526781145d001c63c6c2ea1781145d00065050600080fd50b00002600052602060" - "00f350b0000160005260206000f3" - "600181115d0004506001b160018103b0000181029050b1" - "600281115d0004506001b160028103b0000260018203b00002019050b1"_hex; - - ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{}); + "ef0001 01000c 020003 003b 0017 001d 040000 00 00800004 01010003 01010004" + "60043560003560e01c63c76652678114e1001c63c6c2ea178114e100065050600080fd50e30002600052602060" + "00f350e3000160005260206000f3" + "60018111e10004506001e460018103e3000181029050e4" + "60028111e10004506001e460028103e3000260018203e30002019050e4"_hex; // Call fac(5) const auto calldata_fac = @@ -64,12 +57,26 @@ TEST_P(evm, callf_stack_size_1024) if (is_advanced()) return; - rev = EVMC_CANCUN; - const auto code = bytecode{"ef0001 010008 020002 0BFF 0004 030000 00 000003FF 00000001"_hex} + - 1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP + - OP_RETURN + push(1) + OP_POP + OP_RETF; + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push(1) + OP_CALLF + "0001" + 1021 * OP_POP + OP_RETURN, 1023) + .code(push(1) + OP_POP + OP_RETF, 0, 0, 1); + + execute(bytecode{code}); + EXPECT_STATUS(EVMC_SUCCESS); +} + +TEST_P(evm, callf_with_inputs_stack_size_1024) +{ + // CALLF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push(1) + OP_CALLF + "0001" + 1021 * OP_POP + OP_RETURN, 1023) + .code(push(1) + OP_POP + OP_RETF, 3, 3, 4); - ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success); execute(bytecode{code}); EXPECT_STATUS(EVMC_SUCCESS); } @@ -80,13 +87,28 @@ TEST_P(evm, callf_stack_overflow) if (is_advanced()) return; - rev = EVMC_CANCUN; - const auto code = - bytecode{"ef0001 010008 020002 0BFF 0007 030000 00 000003FF 00000002"_hex} + // EOF header - 1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP + OP_RETURN + - 2 * push(1) + 2 * OP_POP + OP_RETF; + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push(1) + OP_CALLF + "0001" + 1021 * OP_POP + OP_RETURN, 1023) + .code(push(1) + OP_CALLF + "0002" + OP_POP + OP_RETF, 0, 0, 1) + .code(push(1) + OP_POP + OP_RETF, 0, 0, 1); + + execute(bytecode{code}); + EXPECT_STATUS(EVMC_STACK_OVERFLOW); +} + +TEST_P(evm, callf_with_inputs_stack_overflow) +{ + // CALLF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push(1) + OP_CALLF + "0001" + 1021 * OP_POP + OP_RETURN, 1023) + .code(push(1) + OP_CALLF + "0002" + OP_POP + OP_RETF, 3, 3, 4) + .code(push(1) + OP_POP + OP_RETF, 3, 3, 4); - ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success); execute(bytecode{code}); EXPECT_STATUS(EVMC_STACK_OVERFLOW); } @@ -97,13 +119,12 @@ TEST_P(evm, callf_call_stack_size_1024) if (is_advanced()) return; - rev = EVMC_CANCUN; - const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} + - push(1023) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 + - OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 + - OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF; + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode(push(1023) + OP_CALLF + "0001" + OP_STOP, 1) + .code(bytecode{OP_DUP1} + OP_RJUMPI + "0002" + OP_POP + OP_RETF + + push(1) + OP_SWAP1 + OP_SUB + OP_CALLF + "0001" + OP_RETF, + 1, 0, 2); - ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success); execute(bytecode{code}); EXPECT_STATUS(EVMC_SUCCESS); } @@ -114,13 +135,138 @@ TEST_P(evm, callf_call_stack_size_1025) if (is_advanced()) return; - rev = EVMC_CANCUN; - const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} + - push(1024) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 + - OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 + - OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF; + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode(push(1024) + OP_CALLF + "0001" + OP_STOP, 1) + .code(bytecode{OP_DUP1} + OP_RJUMPI + "0002" + OP_POP + OP_RETF + + push(1) + OP_SWAP1 + OP_SUB + OP_CALLF + "0001" + OP_RETF, + 1, 0, 2); - ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success); execute(bytecode{code}); EXPECT_STATUS(EVMC_STACK_OVERFLOW); } + +TEST_P(evm, minimal_jumpf) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(bytecode{OP_JUMPF} + "0001").code(bytecode{OP_STOP}, 0, 0x80, 0); + + execute(bytecode{code}); + EXPECT_STATUS(EVMC_SUCCESS); +} + +TEST_P(evm, jumpf_to_returning_function) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode( + bytecode{OP_CALLF} + "0001" + OP_PUSH0 + OP_MSTORE + OP_PUSH1 + "20" + OP_PUSH0 + OP_RETURN, + 2) + .code(bytecode{OP_PUSH1} + "01" + OP_JUMPF + "0002", 0, 1, 1) + .code(bytecode{OP_PUSH1} + "02" + OP_ADD + OP_RETF, 1, 1, 2); + + execute(bytecode{code}); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(3); +} + +TEST_P(evm, jumpf_to_function_with_fewer_outputs) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode( + bytecode{OP_CALLF} + "0001" + OP_PUSH0 + OP_MSTORE + OP_PUSH1 + "20" + OP_PUSH0 + OP_RETURN, + 3) + .code(push(0xff) + push(0x01) + OP_JUMPF + "0002", 0, 2, 2) + .code(push(0x02) + OP_ADD + OP_RETF, 1, 1, 2); + + execute(bytecode{code}); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(3); +} + +TEST_P(evm, jumpf_stack_size_1024) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push0() + OP_JUMPF + "0001", 1023).code(push0() + OP_STOP, 0, 0x80, 1); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); +} + +TEST_P(evm, jumpf_with_inputs_stack_size_1024) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = + eof_bytecode(1023 * push0() + OP_JUMPF + "0001", 1023).code(push0() + OP_STOP, 3, 0x80, 4); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); +} + +TEST_P(evm, jumpf_stack_overflow) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode(1023 * push0() + OP_JUMPF + "0001", 1023) + .code(push0() + OP_JUMPF + "0002", 0, 0x80, 1) + .code(push0() + OP_STOP, 0, 0x80, 1); + + execute(code); + EXPECT_STATUS(EVMC_STACK_OVERFLOW); +} + +TEST_P(evm, jumpf_with_inputs_stack_overflow) +{ + // JUMPF is not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const bytecode code = eof_bytecode(1023 * push0() + OP_JUMPF + "0001", 1023) + .code(push0() + OP_JUMPF + "0002", 3, 0x80, 4) + .code(push0() + OP_STOP, 3, 0x80, 4); + + ASSERT_EQ(evmone::validate_eof(rev, evmone::ContainerKind::runtime, code), + evmone::EOFValidationError::success); + execute(code); + EXPECT_STATUS(EVMC_STACK_OVERFLOW); +} + +TEST_P(evm, functions_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + auto code = bytecode{OP_CALLF} + "0001" + OP_STOP; + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); + + code = bytecode{OP_RETF}; + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); + + code = bytecode{OP_JUMPF} + "0001"; + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} diff --git a/test/unittests/evm_eof_rjump_test.cpp b/test/unittests/evm_eof_rjump_test.cpp index 9c71ee99..3baf96ff 100644 --- a/test/unittests/evm_eof_rjump_test.cpp +++ b/test/unittests/evm_eof_rjump_test.cpp @@ -3,10 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" -#include "evmone/eof.hpp" - -using evmone::test::evm; +#include +using namespace evmone::test; TEST_P(evm, eof1_rjump) { @@ -14,16 +13,16 @@ TEST_P(evm, eof1_rjump) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2); + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 1); EXPECT_EQ(result.output_data[0], 1); - code = eof1_bytecode( - rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2, "deadbeef"); + code = eof_bytecode(rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2) + .data("deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -37,15 +36,15 @@ TEST_P(evm, eof1_rjump_backward) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2); + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjumpi(10, 1) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 1); EXPECT_EQ(result.output_data[0], 1); - code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2, "deadbeef"); + code = eof_bytecode(rjumpi(10, 1) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2).data("deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -59,8 +58,8 @@ TEST_P(evm, eof1_rjump_0_offset) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(0) + mstore8(0, 1) + ret(0, 1), 2); + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjump(0) + mstore8(0, 1) + ret(0, 1), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -74,8 +73,8 @@ TEST_P(evm, eof1_rjumpi) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode( + rev = EVMC_PRAGUE; + auto code = eof_bytecode( rjumpi(10, calldataload(0)) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1), 2); // RJUMPI condition is true @@ -97,9 +96,9 @@ TEST_P(evm, eof1_rjumpi_backwards) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjumpi(-16, calldataload(0)) + - mstore8(0, 2) + ret(0, 1), + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjumpi(10, 1) + mstore8(0, 1) + ret(0, 1) + + rjumpi(-16, calldataload(0)) + mstore8(0, 2) + ret(0, 1), 2); // RJUMPI condition is true @@ -121,8 +120,8 @@ TEST_P(evm, eof1_rjumpi_0_offset) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjumpi(0, calldataload(0)) + mstore8(0, 1) + ret(0, 1), 2); + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjumpi(0, calldataload(0)) + mstore8(0, 1) + ret(0, 1), 2); // RJUMPI condition is true execute(code, "01"_hex); @@ -143,10 +142,11 @@ TEST_P(evm, eof1_rjumpv_single_offset) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjumpv({3}, 0) + OP_JUMPDEST + OP_JUMPDEST + OP_STOP + 20 + 40 + 0 + - OP_CODECOPY + ret(0, 20), - 3, "ef000101000402000100010300000000000000fe"); + rev = EVMC_PRAGUE; + auto code = eof_bytecode(rjumpv({3}, 0) + OP_JUMPDEST + OP_JUMPDEST + OP_STOP + 20 + 0 + 0 + + OP_DATACOPY + ret(0, 20), + 3) + .data("ef000101000402000100010300000000000000fe"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -161,34 +161,31 @@ TEST_P(evm, eof1_rjumpv_multiple_offsets) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(12) + 10 + 68 + 0 + OP_CODECOPY + ret(0, 10) + - rjumpv({12, -22, 0}, 1) + 10 + 78 + 0 + OP_CODECOPY + ret(0, 10) + - 20 + 68 + 0 + OP_CODECOPY + ret(0, 20), - 3, "ef000101000402000100010300000000000000fe"); + rev = EVMC_PRAGUE; + const auto code = + eof_bytecode(rjumpi(12, 1) + 10 + 0 + 0 + OP_DATACOPY + ret(0, 10) + + rjumpv({12, -23, 0}, calldataload(0)) + 10 + 10 + 0 + OP_DATACOPY + + ret(0, 10) + 20 + 0 + 0 + OP_DATACOPY + ret(0, 20), + 3) + .data("ef000101000402000100010300000000000000fe"); - execute(code); + execute(code, bytes(31, 0) + "01"_hex); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 10); EXPECT_EQ(bytes_view(result.output_data, result.output_size), "ef000101000402000100"_hex); - auto& rjumpv_cond = code[35]; - - rjumpv_cond = 2; - execute(code); + execute(code, bytes(31, 0) + "02"_hex); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 10); EXPECT_EQ(bytes_view(result.output_data, result.output_size), "010300000000000000fe"_hex); - rjumpv_cond = 0; - execute(code); + execute(code, "00"_hex); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 20); EXPECT_EQ(bytes_view(result.output_data, result.output_size), "ef000101000402000100010300000000000000fe"_hex); - rjumpv_cond = 12; // case >= count, same behaviour as for case == 2 - execute(code); + execute(code, bytes(31, 0) + "12"_hex); // case >= count, same behaviour as for case == 2 EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 10); EXPECT_EQ(bytes_view(result.output_data, result.output_size), "010300000000000000fe"_hex); @@ -200,37 +197,55 @@ TEST_P(evm, eof1_rjumpv_long_jumps) if (is_advanced()) return; - rev = EVMC_CANCUN; - auto code = - rjump(0x7fff - 3 - 5) + (0x7fff - 3 - 2 - 8 - 5) * bytecode{OP_JUMPDEST} + 7 + ret_top(); + rev = EVMC_PRAGUE; + const auto code_ret_7 = push(7) + ret_top(); + // code_ret_7 and jumpdests together make up 0x7fff bytes + const auto jumpdests = (0x7fff - static_cast(code_ret_7.size())) * bytecode{OP_JUMPDEST}; - code += rjumpv({-0x7FFF, 0x7FFF - 8 - 2 - 8}, 0) + - (0x7fff - 8 - 2 - 8) * bytecode{OP_JUMPDEST} + 5 + ret_top(); - - code = eof1_bytecode(code, 2); - auto& rjumpv_cond = code[0x7fff - 3 - 5 + 3 + 1 + 19]; + auto code = eof_bytecode(rjumpi(0x7fff, 1) + jumpdests + code_ret_7 + + rjumpv({-0x7fff}, calldataload(0)) + 5 + ret_top(), + 2); - execute(code); + execute(code, "00"_hex); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(7); - rjumpv_cond = 1; + execute(code, "01"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(5); + + code = + eof_bytecode(rjumpv({0x7FFF}, calldataload(0)) + jumpdests + code_ret_7 + 5 + ret_top(), 2); - execute(code); + execute(code, bytes(31, 0) + "00"_hex); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(5); + + execute(code, bytes(31, 0) + "01"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(7); } -TEST_P(evm, rjumps_undefined_in_legacy) +TEST_P(evm, rjump_undefined_in_legacy) { - rev = EVMC_CANCUN; - auto code = rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1); - + rev = EVMC_PRAGUE; + const auto code = rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1); execute(code); EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} - code = rjumpi(10, 1) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1); +TEST_P(evm, rjumpi_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + const auto code = rjumpi(10, 1) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1); + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} +TEST_P(evm, rjumpv_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + const auto code = rjumpv({0}, calldataload(0)) + OP_STOP; execute(code); EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); } diff --git a/test/unittests/evm_eof_test.cpp b/test/unittests/evm_eof_test.cpp index dc31cbc2..4b6105b1 100644 --- a/test/unittests/evm_eof_test.cpp +++ b/test/unittests/evm_eof_test.cpp @@ -3,149 +3,319 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" -#include "evmone/eof.hpp" +#include -using evmone::test::evm; +using namespace evmc::literals; +using namespace evmone::test; TEST_P(evm, eof1_execution) { - const auto code = eof1_bytecode(OP_STOP); + const auto code = eof_bytecode(OP_STOP); - rev = EVMC_SHANGHAI; + rev = EVMC_CANCUN; execute(code); EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; execute(code); EXPECT_STATUS(EVMC_SUCCESS); } TEST_P(evm, eof1_execution_with_data_section) { - rev = EVMC_CANCUN; + rev = EVMC_PRAGUE; // data section contains ret(0, 1) - const auto code = eof1_bytecode(mstore8(0, 1) + OP_STOP, 2, ret(0, 1)); + const auto code = eof_bytecode(mstore8(0, 1) + OP_STOP, 2).data(ret(0, 1)); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(result.output_size, 0); } -TEST_P(evm, eof1_codesize) +TEST_P(evm, eof_data_only_contract) { - rev = EVMC_CANCUN; - auto code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1), 2); + rev = EVMC_PRAGUE; + auto code = "EF0001 010004 020001 0001 04daaa 00 00800000 FE"_hex; + const auto data_size_ptr = &code[code.find(0xda)]; + intx::be::unsafe::store(data_size_ptr, uint16_t{0}); execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 28); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); - code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1), 2, "deadbeef"); + intx::be::unsafe::store(data_size_ptr, uint16_t{1}); + execute(code + "aa"_hex); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 32); + intx::be::unsafe::store(data_size_ptr, uint16_t{256}); + execute(code + bytes(256, 0x01)); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); } -TEST_P(evm, eof1_codecopy_full) +TEST_P(evm, eof1_dataload) { - rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{31} + 0 + 0 + OP_CODECOPY + ret(0, 31), 3); + // Data instructions are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + // data is 64 bytes long + const auto data = bytes(8, 0x0) + bytes(8, 0x11) + bytes(8, 0x22) + bytes(8, 0x33) + + bytes(8, 0xaa) + bytes(8, 0xbb) + bytes(8, 0xcc) + bytes(8, 0xdd); + const auto code = eof_bytecode(calldataload(0) + OP_DATALOAD + ret_top(), 2).data(data); + + // DATALOAD(0) + execute(code, "00"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000111111111111111122222222222222223333333333333333"_hex); - execute(code); + // DATALOAD(1) + execute(code, "0000000000000000000000000000000000000000000000000000000000000001"_hex); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c0300000000000003601f6000600039601f6000f3"_hex); + "00000000000000111111111111111122222222222222223333333333333333aa"_hex); - code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35), 3, "deadbeef"); + // DATALOAD(2) + execute(code, "0000000000000000000000000000000000000000000000000000000000000020"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd"_hex); - execute(code); + // DATALOAD(33) - truncated word + execute(code, "0000000000000000000000000000000000000000000000000000000000000021"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "aaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd00"_hex); + + // DATALOAD(64) - out of data bounds + execute(code, "0000000000000000000000000000000000000000000000000000000000000040"_hex); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000400000000036023600060003960236000f3deadbeef"_hex); + "0000000000000000000000000000000000000000000000000000000000000000"_hex); + + // DATALOAD(u256_max) - out of data bounds + execute(code, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000000"_hex); } -TEST_P(evm, eof1_codecopy_header) +TEST_P(evm, eof1_dataloadn) { - rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), 3); + // Data instructions are not implemented in Advanced. + if (is_advanced()) + return; + rev = EVMC_PRAGUE; + // data is 64 bytes long + const auto data = bytes(8, 0x0) + bytes(8, 0x11) + bytes(8, 0x22) + bytes(8, 0x33) + + bytes(8, 0xaa) + bytes(8, 0xbb) + bytes(8, 0xcc) + bytes(8, 0xdd); + + // DATALOADN{0} + auto code = eof_bytecode(bytecode(OP_DATALOADN) + "0000" + ret_top(), 2).data(data); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ( - bytes_view(result.output_data, result.output_size), "ef0001010004020001000c03000000"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000111111111111111122222222222222223333333333333333"_hex); - code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), 3, "deadbeef"); + // DATALOADN{1} + code = eof_bytecode(bytecode(OP_DATALOADN) + "0001" + ret_top(), 2).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "00000000000000111111111111111122222222222222223333333333333333aa"_hex); + // DATALOADN{32} + code = eof_bytecode(bytecode(OP_DATALOADN) + "0020" + ret_top(), 2).data(data); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ( - bytes_view(result.output_data, result.output_size), "ef0001010004020001000c03000400"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd"_hex); } -TEST_P(evm, eof1_codecopy_code) +TEST_P(evm, eof1_datasize) { - rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12), 3); + // Data instructions are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + + // no data section + auto code = eof_bytecode(bytecode(OP_DATASIZE) + ret_top(), 2); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000000"_hex); + code = eof_bytecode(bytecode(OP_DATASIZE) + ret_top(), 2).data(bytes{0x0}); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c6013600039600c6000f3"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000001"_hex); - code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12), 3, "deadbeef"); + code = eof_bytecode(bytecode(OP_DATASIZE) + ret_top(), 2).data(bytes(32, 0x0)); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000020"_hex); + code = eof_bytecode(bytecode(OP_DATASIZE) + ret_top(), 2).data(bytes(64, 0x0)); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c6013600039600c6000f3"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000040"_hex); + + code = eof_bytecode(bytecode(OP_DATASIZE) + ret_top(), 2).data(bytes(80, 0x0)); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000050"_hex); } -TEST_P(evm, eof1_codecopy_data) +TEST_P(evm, eof1_datacopy) { - rev = EVMC_CANCUN; + // Data instructions are not implemented in Advanced. + if (is_advanced()) + return; - const auto code = eof1_bytecode(bytecode{4} + 31 + 0 + OP_CODECOPY + ret(0, 4), 3, "deadbeef"); + rev = EVMC_PRAGUE; + // data is 64 bytes long + const auto data = bytes(8, 0x0) + bytes(8, 0x11) + bytes(8, 0x22) + bytes(8, 0x33) + + bytes(8, 0xaa) + bytes(8, 0xbb) + bytes(8, 0xcc) + bytes(8, 0xdd); + auto code = eof_bytecode(bytecode(1) + 0 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), "deadbeef"_hex); -} + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000000"_hex); -TEST_P(evm, eof1_codecopy_out_of_bounds) -{ - // 4 bytes out of container bounds - result is implicitly 0-padded - rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35), 3); + code = eof_bytecode(bytecode(1) + 8 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "1100000000000000000000000000000000000000000000000000000000000000"_hex); + code = eof_bytecode(bytecode(1) + 63 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000000000000036023600060003960236000f300000000"_hex); + "dd00000000000000000000000000000000000000000000000000000000000000"_hex); - code = eof1_bytecode(bytecode{39} + 0 + 0 + OP_CODECOPY + ret(0, 39), 3, "deadbeef"); + code = eof_bytecode(bytecode(0) + 64 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000000"_hex); + + code = eof_bytecode(bytecode(16) + 8 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "1111111111111111222222222222222200000000000000000000000000000000"_hex); + code = eof_bytecode(bytecode(32) + 8 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000400000000036027600060003960276000f3deadbeef00000000"_hex); + "111111111111111122222222222222223333333333333333aaaaaaaaaaaaaaaa"_hex); + + code = eof_bytecode(bytecode(8) + 63 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "dd00000000000000000000000000000000000000000000000000000000000000"_hex); + + code = eof_bytecode(bytecode(0) + 65 + 0 + OP_DATACOPY + ret(0, 32), 3).data(data); + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0000000000000000000000000000000000000000000000000000000000000000"_hex); } -TEST_P(evm, eof_data_only_contract) +TEST_P(evm, datacopy_memory_cost) +{ + // Data instructions are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const auto data = bytes{0}; + const auto code = eof_bytecode(bytecode(1) + 0 + 0 + OP_DATACOPY + OP_STOP, 3).data(data); + execute(18, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 18); + + execute(17, code); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, eof_eofcreate) +{ + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); + const bytecode deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + OP_CALLDATASIZE + 0 + OP_RETURNCONTRACT + Opcode{0}; + const auto init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto create_code = calldatacopy(0, 0, OP_CALLDATASIZE) + + eofcreate().input(0, OP_CALLDATASIZE).salt(0xff) + ret_top(); + const auto container = eof_bytecode(create_code, 4).container(init_container); + + // test executing create code mocking EOFCREATE call + host.call_result.output_data = deploy_container.data(); + host.call_result.output_size = deploy_container.size(); + host.call_result.create_address = 0xcc010203040506070809010203040506070809ce_address; + + execute(container, aux_data); + EXPECT_STATUS(EVMC_SUCCESS); + + ASSERT_EQ(host.recorded_calls.size(), 1); + const auto& call_msg = host.recorded_calls.back(); + + EXPECT_EQ(call_msg.input_size, aux_data.size()); + + ASSERT_EQ(result.output_size, 32); + EXPECT_EQ(output, "000000000000000000000000cc010203040506070809010203040506070809ce"_hex); +} + +TEST_P(evm, eofcreate_undefined_in_legacy) { rev = EVMC_CANCUN; - auto code = "EF0001 010004 020001 0001 03daaa 00 00000000 FE"_hex; - const auto data_size_ptr = &code[code.find(0xda)]; + const auto code = calldatacopy(0, 0, OP_CALLDATASIZE) + + eofcreate().input(0, OP_CALLDATASIZE).salt(0xff) + ret_top(); - intx::be::unsafe::store(data_size_ptr, uint16_t{0}); execute(code); - EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} - intx::be::unsafe::store(data_size_ptr, uint16_t{1}); - execute(code + "aa"_hex); - EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); +TEST_P(evm, returncontract_undefined_in_legacy) +{ + rev = EVMC_CANCUN; + const auto code = + calldatacopy(0, 0, OP_CALLDATASIZE) + OP_CALLDATASIZE + 0 + OP_RETURNCONTRACT + Opcode{0}; - intx::be::unsafe::store(data_size_ptr, uint16_t{256}); - execute(code + bytes(256, 0x01)); - EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} + +TEST_P(evm, eofcreate_staticmode) +{ + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + msg.flags |= EVMC_STATIC; + const auto code = eof_bytecode(4 * push0() + OP_EOFCREATE + "00" + OP_STOP, 4) + .container(eof_bytecode(push0() + push0() + OP_REVERT, 2)); + execute(code); + EXPECT_EQ(result.status_code, EVMC_STATIC_MODE_VIOLATION); + EXPECT_EQ(result.gas_left, 0); } diff --git a/test/unittests/evm_fixture.hpp b/test/unittests/evm_fixture.hpp index ff6418ab..8cff773d 100644 --- a/test/unittests/evm_fixture.hpp +++ b/test/unittests/evm_fixture.hpp @@ -3,16 +3,16 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "evmone/eof.hpp" #include #include #include #include - #include +#include #include #include #include -#include #define EXPECT_STATUS(STATUS_CODE) \ EXPECT_EQ(result.status_code, STATUS_CODE); \ @@ -106,6 +106,7 @@ class evm : public testing::TestWithParam /// The `gas_used` field will be updated accordingly. void execute(int64_t gas, const bytecode& code, bytes_view input = {}) noexcept { + result = evmc::Result{}; host.eos_evm_version = eos_evm_version; msg.input_data = input.data(); @@ -118,30 +119,62 @@ class evm : public testing::TestWithParam host.access_account(msg.recipient); } + if (rev >= EVMC_PRAGUE && is_eof_container(code)) + { + ASSERT_EQ(get_error_message(validate_eof(rev, ContainerKind::runtime, code)), + get_error_message(EOFValidationError::success)); + } + + if(!is_advanced()) { - evmone::ExecutionState state; - state.reset(msg, rev, evmc::MockedHost::get_interface(), host.to_context(), code, gas_params, eos_evm_version); - auto& evm_ = *static_cast(vm.get_raw_pointer()); - auto analysis = evmone::baseline::analyze(rev, code); - result = evmc::Result{evmone::baseline::execute(evm_, gas, state, analysis)}; + + auto& evm_ = *static_cast(vm.get_raw_pointer()); + const bytes_view container = code; + const auto eof_enabled = rev >= instr::REV_EOF1; + + // Since EOF validation recurses into subcontainers, it only makes sense to do for top level + // message calls. The condition for `msg->kind` inside differentiates between creation tx code + // (initcode) and already deployed code (runtime). + if (evm_.validate_eof && eof_enabled && is_eof_container(container) && msg.depth == 0) + { + const auto container_kind = + (msg.kind == EVMC_EOFCREATE ? ContainerKind::initcode : ContainerKind::runtime); + if (validate_eof(rev, container_kind, container) != EOFValidationError::success) + result = evmc::Result{evmc_make_result(EVMC_CONTRACT_VALIDATION_FAILURE, 0, 0, 0, 0, 0, nullptr, 0)}; + } + if( result.status_code != EVMC_CONTRACT_VALIDATION_FAILURE ) { + const auto code_analysis = evmone::baseline::analyze(container, eof_enabled); + evmone::ExecutionState state; + state.reset(msg, rev, evmc::MockedHost::get_interface(), host.to_context(), code, gas_params, eos_evm_version); + result = evmc::Result{evmone::baseline::execute(evm_, msg, code_analysis, state)}; + } } else { - evmone::advanced::AdvancedExecutionState state; - state.reset(msg, rev, evmc::MockedHost::get_interface(), host.to_context(), code, gas_params, eos_evm_version); evmone::advanced::AdvancedCodeAnalysis analysis; - const bytes_view container = {code.data(), code.size()}; - if (is_eof_container(container)) { - if (rev >= EVMC_CANCUN) { + const bytes_view container = code; + if (is_eof_container(container)) + { + if (rev >= EVMC_PRAGUE) + { const auto eof1_header = read_valid_eof1_header(container); analysis = evmone::advanced::analyze(rev, eof1_header.get_code(container, 0)); - } else { - result = evmc::Result{evmc::make_result(EVMC_UNDEFINED_INSTRUCTION, 0, 0, 0, 0, 0, nullptr, 0)}; - return; } - } else { + else{ + // Skip analysis, because it will recognize 01 section id as OP_ADD and return + // EVMC_STACKUNDERFLOW. + result = evmc::Result{evmc::make_result(EVMC_UNDEFINED_INSTRUCTION, 0, 0, 0l, 0l, 0l, nullptr, 0)}; + } + } + else { analysis = evmone::advanced::analyze(rev, container); } - result = evmc::Result{evmone::advanced::execute(state, analysis)}; + + if( result.status_code != EVMC_UNDEFINED_INSTRUCTION ) { + evmone::advanced::AdvancedExecutionState state; + state.reset(msg, rev, evmc::MockedHost::get_interface(), host.to_context(), code, gas_params, eos_evm_version); + result = evmc::Result{evmone::advanced::execute(state, analysis)}; + } } + output = {result.output_data, result.output_size}; gas_used = msg.gas - result.gas_left; } diff --git a/test/unittests/evm_memory_test.cpp b/test/unittests/evm_memory_test.cpp index 2ef00855..b9f82c16 100644 --- a/test/unittests/evm_memory_test.cpp +++ b/test/unittests/evm_memory_test.cpp @@ -4,13 +4,12 @@ #include "evm_fixture.hpp" #include -#include #include using namespace evmone; using namespace evmc::literals; using namespace intx; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, memory_and_not) { @@ -121,6 +120,9 @@ memory_access_opcode memory_access_opcodes[] = { {OP_MLOAD, 0, -1}, {OP_MSTORE, 0, -1}, {OP_MSTORE8, 0, -1}, + {OP_MCOPY, 0, 2}, + {OP_MCOPY, 1, 2}, + {OP_DATACOPY, 0, 2}, {OP_EXTCODECOPY, 1, 3}, {OP_RETURNDATACOPY, 0, 2}, {OP_LOG0, 0, 1}, @@ -176,7 +178,7 @@ TEST_P(evm, memory_access) { // This test checks if instructions accessing memory properly respond with out-of-gas // error for combinations of memory offset and memory size arguments. - rev = EVMC_CONSTANTINOPLE; + rev = EVMC_PRAGUE; for (const auto& p : memory_access_test_cases) { @@ -206,6 +208,15 @@ TEST_P(evm, memory_access) code += bytecode{t.opcode}; + if (t.opcode == OP_DATACOPY) + { + if (is_advanced()) + continue; + + code += bytecode{OP_STOP}; + code = eof_bytecode(code, 3).data(bytes(32, 0)); + } + const auto gas = 8796294610952; execute(gas, code); @@ -238,3 +249,65 @@ TEST_P(evm, memory_access) } } } + +TEST_P(evm, mcopy) +{ + rev = EVMC_CANCUN; + bytecode s; + s += mstore(0, push(0x0123456789abcdef000000000000000000000000000000000000000000000000_u256)) + + mcopy(32, 0, 8) + ret(32, 8); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 8); + EXPECT_EQ(bytes_view(&result.output_data[0], 8), "0123456789abcdef"_hex); + + // copy from uninitialized memory + s = mcopy(0, 24, 16) + ret(0, 16); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 16); + EXPECT_EQ(bytes_view(&result.output_data[0], 16), "00000000000000000000000000000000"_hex); + + // copy from initialized + uninitialized memory + s = mstore(0, push(0x0000000000000000000000000000000000000000000000000123456789abcdef_u256)) + + mcopy(64, 24, 16) + ret(64, 16); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 16); + EXPECT_EQ(bytes_view(&result.output_data[0], 16), "0123456789abcdef0000000000000000"_hex); + + // overlapping src < dst + s = mstore(0, push(0x0123456789abcdef000000000000000000000000000000000000000000000000_u256)) + + mcopy(4, 0, 8) + ret(0, 16); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 16); + EXPECT_EQ(bytes_view(&result.output_data[0], 16), "012345670123456789abcdef00000000"_hex); + + // overlapping src > dst + s = mstore(0, push(0x00112233445566778899aabbccddeeff00000000000000000000000000000000_u256)) + + mcopy(0, 4, 8) + ret(0, 16); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 16); + EXPECT_EQ(bytes_view(&result.output_data[0], 16), "445566778899aabb8899aabbccddeeff"_hex); + + // overlapping src == dst + s = mstore(0, push(0x00112233445566778899aabbccddeeff00000000000000000000000000000000_u256)) + + mcopy(4, 4, 8) + ret(0, 16); + execute(s); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 16); + EXPECT_EQ(bytes_view(&result.output_data[0], 16), "00112233445566778899aabbccddeeff"_hex); +} + +TEST_P(evm, mcopy_memory_cost) +{ + rev = EVMC_CANCUN; + const auto code = mcopy(0, 0, 1); + execute(18, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 18); + + execute(17, code); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} diff --git a/test/unittests/evm_other_test.cpp b/test/unittests/evm_other_test.cpp index a657bc1d..6eb46e6e 100644 --- a/test/unittests/evm_other_test.cpp +++ b/test/unittests/evm_other_test.cpp @@ -9,7 +9,7 @@ #include "evm_fixture.hpp" -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, evmone_loaded_program_relocation) { diff --git a/test/unittests/evm_state_test.cpp b/test/unittests/evm_state_test.cpp index 1ec330ef..4ea41e51 100644 --- a/test/unittests/evm_state_test.cpp +++ b/test/unittests/evm_state_test.cpp @@ -9,7 +9,7 @@ #include using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, code) { @@ -432,7 +432,7 @@ TEST_P(evm, blockhash) host.block_hash.bytes[13] = 0x13; host.tx_context.block_number = 0; - const auto code = push(0) + OP_BLOCKHASH + ret_top(); + const auto code = blockhash(0) + ret_top(); execute(code); EXPECT_EQ(result.status_code, EVMC_SUCCESS); EXPECT_EQ(gas_used, 38); @@ -496,9 +496,10 @@ TEST_P(evm, extcodecopy_big_index) TEST_P(evm, extcodehash) { - auto& hash = host.accounts[{}].codehash; - std::fill(std::begin(hash.bytes), std::end(hash.bytes), uint8_t{0xee}); + static constexpr auto HASH = + 0xe10000a00000000b0000000000c00000000d00000e0000f0000000000000001e_bytes32; + host.accounts[{}].codehash = HASH; const auto code = push(0) + OP_EXTCODEHASH + ret_top(); rev = EVMC_BYZANTIUM; @@ -509,9 +510,7 @@ TEST_P(evm, extcodehash) execute(code); EXPECT_EQ(gas_used, 418); ASSERT_EQ(result.output_size, 32); - auto expected_hash = bytes(32, 0xee); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), - bytes_view(std::begin(hash.bytes), std::size(hash.bytes))); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), HASH); } TEST_P(evm, codecopy_empty) diff --git a/test/unittests/evm_storage_test.cpp b/test/unittests/evm_storage_test.cpp index d0444c5a..eb1fd998 100644 --- a/test/unittests/evm_storage_test.cpp +++ b/test/unittests/evm_storage_test.cpp @@ -8,7 +8,7 @@ #include using namespace evmc::literals; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, storage) { diff --git a/test/unittests/evm_test.cpp b/test/unittests/evm_test.cpp index ef62f3b3..e150bd4b 100644 --- a/test/unittests/evm_test.cpp +++ b/test/unittests/evm_test.cpp @@ -3,12 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" +#include #include using namespace evmc::literals; -using namespace evmone; using namespace intx; -using evmone::test::evm; +using namespace evmone::test; TEST_P(evm, empty) { @@ -641,10 +641,7 @@ TEST_P(evm, undefined_instruction_analysis_overflow) { rev = EVMC_PETERSBURG; - auto undefined_opcode = static_cast(0x0c); - auto code = bytecode{undefined_opcode}; - - execute(code); + execute(bytecode{"0c"}); // undefined opcode EXPECT_EQ(result.status_code, EVMC_UNDEFINED_INSTRUCTION); } @@ -695,9 +692,8 @@ TEST_P(evm, staticmode) TEST_P(evm, max_code_size_push1) { - constexpr auto max_code_size = 0x6000; - const auto code = (max_code_size / 2) * push(1); - ASSERT_EQ(code.size(), max_code_size); + const auto code = (evmone::MAX_CODE_SIZE / 2) * push(1); + ASSERT_EQ(code.size(), evmone::MAX_CODE_SIZE); execute(code); EXPECT_STATUS(EVMC_STACK_OVERFLOW); diff --git a/test/unittests/evmmax_bn254_add_test.cpp b/test/unittests/evmmax_bn254_add_test.cpp new file mode 100644 index 00000000..18cfa92b --- /dev/null +++ b/test/unittests/evmmax_bn254_add_test.cpp @@ -0,0 +1,162 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evmone_precompiles/bn254.hpp" +#include +#include + +using namespace evmmax::bn254; +using namespace evmone::test; + +namespace +{ +struct TestCase +{ + bytes input; + bytes expected_output; + + TestCase(bytes i, bytes o) : input{std::move(i)}, expected_output{std::move(o)} + { + input.resize(128); + expected_output.resize(64); + } +}; + +const TestCase + test_cases + [] = + { + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4"_hex, + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f"_hex}, + {"1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"_hex}, + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex, + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex}, + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex, + "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4"_hex}, + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "059a381fec09e29448a58ae8905f41d1eb8ff0ed755aa0f827821aefde02ec7d269d2516bf8c4f5798cc1267162e59add561e5537a328fe0f28a252fa287a72a"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex, + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex}, + {"1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d40f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba"_hex, + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db81f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db81f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db8"_hex, + "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db8"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83"_hex}, + {"030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf005acb4b400e90c0063006a39f478f3e865e306dd5cd56f356e2e8cd8fe7edae6"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "113aeccecdaf57cd8c0aace591774949dcdaf892555fa86726fa7e679b89c0670bffba84127a19abde488a8251a9a3fce33b34a76f96aafb11ab4a6cef3e9979"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad"_hex, + "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b29ce3d80a74ddc13784beb25ca9fbfd048a3265a32c6f38b92060c5093a0e7a7"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "1d78954c630b3895fbbfafac1294f2c0158879fdc70bfe18222890e7bfb66fba101c3346e98b136a7078aebd427dced763722d77e3d7985342e0bffcc6ea4d56"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b02f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0"_hex, + "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1eed5d5325c31fc89dd541a13d7f63b981fae8d4bf78a6b08a38a601fcfea97b"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0"_hex}, + {"030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf02ab799bee0489429554fdb7c8d086475319e63b40b9c5b57cdf1ff3dd9fe2261"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "113aeccecdaf57cd8c0aace591774949dcdaf892555fa86726fa7e679b89c067246493eeceb7867dda07bb342fd7b460b44635e9f8db1f922a7541a9e93e63ce"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a"_hex, + "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b069610f239e3c41640045a90b6e1988d4ede443735aad701aa1a7fc644dc15a0"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "1d78954c630b3895fbbfafac1294f2c0158879fdc70bfe18222890e7bfb66fba20481b2bf7a68cbf47d796f93f038986340f3d19849a3239f93fcc1a1192aff1"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a972f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97"_hex, + "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1176f11fbb6e80611a7b04154401f4a4158681bca8f923dcb1e7e614db7e53cc"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a9700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"_hex}, + {"17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98"_hex, + "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f"_hex}, +}; +} // namespace + +TEST(evmmax, bn254_validate_inputs) +{ + for (const auto& t : test_cases) + { + ASSERT_EQ(t.input.size(), 128); + ASSERT_EQ(t.expected_output.size(), 64); + + const Point a{ + be::unsafe::load(t.input.data()), be::unsafe::load(&t.input[32])}; + const Point b{ + be::unsafe::load(&t.input[64]), be::unsafe::load(&t.input[96])}; + const Point e{be::unsafe::load(t.expected_output.data()), + be::unsafe::load(&t.expected_output[32])}; + + EXPECT_TRUE(validate(a)); + EXPECT_TRUE(validate(b)); + EXPECT_TRUE(validate(e)); + } +} + +TEST(evmmax, bn254_pt_add) +{ + for (const auto& t : test_cases) + { + const Point a{ + be::unsafe::load(t.input.data()), be::unsafe::load(&t.input[32])}; + const Point b{ + be::unsafe::load(&t.input[64]), be::unsafe::load(&t.input[96])}; + const Point e{be::unsafe::load(t.expected_output.data()), + be::unsafe::load(&t.expected_output[32])}; + + EXPECT_EQ(add(a, b), e); + } +} + +TEST(evmmax, inv_1) +{ + const evmmax::ModArith m{evmmax::bn254::FieldPrime}; + + const auto a = + m.to_mont(0x6e140df17432311190232a91a38daed3ee9ed7f038645dd0278da7ca6e497de_u256); + + const auto a_inv = field_inv(m, a); + const auto p = m.mul(a, a_inv); + EXPECT_EQ(m.from_mont(p), 1); +} diff --git a/test/unittests/evmmax_bn254_mul_test.cpp b/test/unittests/evmmax_bn254_mul_test.cpp new file mode 100644 index 00000000..1abdee6c --- /dev/null +++ b/test/unittests/evmmax_bn254_mul_test.cpp @@ -0,0 +1,244 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evmone_precompiles/bn254.hpp" +#include +#include + +using namespace evmmax::bn254; +using namespace evmone::test; + +namespace +{ +struct TestCase +{ + bytes input; + bytes expected_output; + + TestCase(bytes i, bytes o) : input{std::move(i)}, expected_output{std::move(o)} + { + input.resize(96); + expected_output.resize(64); + } +}; + +const TestCase + test_cases + [] = + { + {"025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3"_hex, + "14789d0d4a730b354403b5fac948113739e276c23e0258d8596ee72f9cd9d3230af18a63153e0ec25ff9f2951dd3fa90ed0197bfef6e2a1a62b5095b9d2b4a27"_hex}, // from https://github.com/sedaprotocol/bn254/blob/main/src/bn256.json#L68 + {"070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"_hex, + "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e"_hex}, // https://github.com/sedaprotocol/bn254/blob/main/src/bn256.json#LL62C1-L62C1 + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba0000000000000000000000000000000000000000000000000000000000000003"_hex, + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000001"_hex, + "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000100000000000000000000000000000000"_hex, + "13b8fec4a1eb2c7e3ccc07061ad516277c3bbe57bd4a302012b58a517f6437a4224d978b5763831dff16ce9b2c42222684835fedfc70ffec005789bb0c10de36"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"_hex, + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000"_hex, + "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex, + "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a97"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000"_hex, + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009"_hex, + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000001"_hex, + "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000100000000000000000000000000000000"_hex, + "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000001"_hex, + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"_hex, + "03d64e49ebb3c56c99e0769c1833879c9b86ead23945e1e7477cbd057e961c500d6840b39f8c2fefe0eced3e7d210b830f50831e756f1cc9039af65dc292e6d0"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000002"_hex, + "03d64e49ebb3c56c99e0769c1833879c9b86ead23945e1e7477cbd057e961c500d6840b39f8c2fefe0eced3e7d210b830f50831e756f1cc9039af65dc292e6d0"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"_hex, + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000"_hex, + "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex, + "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f600000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000"_hex, + "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575"_hex}, + {"1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000009"_hex, + "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575"_hex}, + {"0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba0000000000000000000000000000000000000000000000000000000000000002"_hex, + "1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2860217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f0000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db80000000000000000000000000000000000000000000000000000000000000002"_hex, + "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"_hex, + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c492eddcb59a6517e86bfbe35c9691479fffc6e0580000ca2706c983ff7afcb1db8"_hex}, + {"1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff"_hex, + "255e468453d7636cc1563e43f7521755f95e6c56043c7321b4ae04e772945fb00225c5f1623620fd84bfbab2d861a9d1e570f7727c540f403085998ebaf407c4"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450000000000000000000000000000000000000000000000000000000000000002"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450000000000000000000000000000000000000000000000000000000000000003"_hex, + "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf005acb4b400e90c0063006a39f478f3e865e306dd5cd56f356e2e8cd8fe7edae6"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450000000000000000000000000000000000000000000000000000000000000001"_hex, + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad0000000000000000000000000000000000000000000000000000000000000002"_hex, + "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b29ce3d80a74ddc13784beb25ca9fbfd048a3265a32c6f38b92060c5093a0e7a7"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"_hex, + "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e5628069ef5e376c0a1ea82f9dfc2e0001a7f385d655eef9a6f976c7a5d2c493ea3ad"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "2c15ed1902e189486ab6b625aa982510aef6246b21a1e1bcea382da4d735e8ba02103e58cbd2fa8081763442ab46c26a9b8051e9b049c3948c8d7d0e139c5e3f"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b00000000000000000000000000000000000000000000000000000000000000002"_hex, + "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1eed5d5325c31fc89dd541a13d7f63b981fae8d4bf78a6b08a38a601fcfea97b"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex, + "2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d3521d701ec9e3fca50e84777f0f68caff5bff48cf6a6bd4428462ae9366cf0582b0"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"_hex, + "08e2142845db159bd105879a109fe7a6f254ed3ddae0e9cd8a2aeae05e5f647b221108ee615499d2e0a1113ca1a858a34e055f9da2d30e6e6ab392b049944a92"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"_hex, + "0769bf9ac56bea3ff40232bcb1b6bd159315d84715b8e679f2d355961915abf02ab799bee0489429554fdb7c8d086475319e63b40b9c5b57cdf1ff3dd9fe2261"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff"_hex, + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, + {"0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a0000000000000000000000000000000000000000000000000000000000000002"_hex, + "1fd3b816d9951dcb9aa9797d25e51a865987703ae83cd69c4658679f0350ae2b069610f239e3c41640045a90b6e1988d4ede443735aad701aa1a7fc644dc15a0"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"_hex, + "0ccbec17235f5b9cc5e42f3df6364a76ecdd0101ddda8fc5dc0ba0b59c0e562829c5588f6a70fe3f355665f3a1813dde5f24053278d75af5cfa62eea8f3e599a"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"_hex, + "2c15ed1902e189486ab6b625aa982510aef6246b21a1e1bcea382da4d735e8ba2e54101a155ea5a936da1173d63a95f2fc0118a7b82806f8af930f08c4e09f08"_hex}, + {"2f588cffe99db877a4434b598ab28f81e0522910ea52b45f0adaa772b2d5d35212f42fa8fd34fb1b33d8c6a718b6590198389b26fc9d8808d971f8b009777a970000000000000000000000000000000000000000000000000000000000000002"_hex, + "2fa739d4cde056d8fd75427345cbb34159856e06a4ffad64159c4773f23fbf4b1176f11fbb6e80611a7b04154401f4a4158681bca8f923dcb1e7e614db7e53cc"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"_hex, + "08e2142845db159bd105879a109fe7a6f254ed3ddae0e9cd8a2aeae05e5f647b0e5345847fdd0656d7af3479dfd8ffba497c0af3c59ebc1ed16cf9668ee8b2b5"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex}, + {"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"_hex, + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"_hex}, +}; +} // namespace + +TEST(evmmax, bn254_mul_validate_inputs) +{ + for (const auto& t : test_cases) + { + ASSERT_EQ(t.input.size(), 96); + ASSERT_EQ(t.expected_output.size(), 64); + + const Point a{ + be::unsafe::load(t.input.data()), be::unsafe::load(&t.input[32])}; + const Point e{be::unsafe::load(t.expected_output.data()), + be::unsafe::load(&t.expected_output[32])}; + + EXPECT_TRUE(validate(a)); + EXPECT_TRUE(validate(e)); + } +} + +TEST(evmmax, bn254_pt_mul) +{ + for (const auto& t : test_cases) + { + const Point p{ + be::unsafe::load(t.input.data()), be::unsafe::load(&t.input[32])}; + const auto d{be::unsafe::load(&t.input[64])}; + const Point e{be::unsafe::load(t.expected_output.data()), + be::unsafe::load(&t.expected_output[32])}; + + auto r = mul(p, d); + + EXPECT_EQ(r, e); + } +} diff --git a/test/unittests/evmmax_secp256k1_test.cpp b/test/unittests/evmmax_secp256k1_test.cpp new file mode 100644 index 00000000..fb7f27a5 --- /dev/null +++ b/test/unittests/evmmax_secp256k1_test.cpp @@ -0,0 +1,381 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace evmmax::secp256k1; +using namespace evmc::literals; +using namespace evmone::test; + +TEST(secp256k1, field_inv) +{ + const evmmax::ModArith m{FieldPrime}; + + for (const auto& t : std::array{ + 1_u256, + 0x6e140df17432311190232a91a38daed3ee9ed7f038645dd0278da7ca6e497de_u256, + FieldPrime - 1, + }) + { + ASSERT_LT(t, FieldPrime); + const auto a = m.to_mont(t); + const auto a_inv = field_inv(m, a); + const auto p = m.mul(a, a_inv); + EXPECT_EQ(m.from_mont(p), 1); + } +} + +TEST(secp256k1, field_sqrt) +{ + const evmmax::ModArith m{FieldPrime}; + + for (const auto& t : std::array{ + 1_u256, + 0x6e140df17432311190232a91a38daed3ee9ed7f038645dd0278da7ca6e497de_u256, + 0xf3b9accc43dc8919ba3b4f1e14c8f7c72e7c4c013a404e9fd35e9c9a5b7b228_u256, + 0x3db99f8c1e729de4c9a283e8714b9f6bc3ef22ac5fd70daaa88b73dcf52ebe9_u256, + 0x37ec7e48f17a78e38d7b3c77d15be8c4a8e6bae83971fdec3b25f861be4b7da_u256, + 0x5b1a739f853ba7e4c6a2f3e91c7b2f7c87d4c0d98ba2fde82a79f3e5d8b76b9_u256, + 0x69187a3b9c5de9e4783a29df87b6f8c5d3a2b6d98c5d7ea1b28f7e5d9a7b6b8_u256, + 0x7a98763a85df9e7c6a28d9f7b6f8d5c3a2b7c6d98c5d7e9a1b2f7d6e5a9b7b6_u256, + 0x8b87953a75ef8d7b5a27c8e7a6f7d4b2a1b6c5d87b5c6d89a0b1e6d4a8a6b5_u256, + 0x9c76942a65df7c6a4a16b7d6a5f6c3a0b0c4b5c76a4b5c78a9f6d3c4a7a5b4_u256, + 0xad65931a55cf6b594915a6c5a4f5b2a9f0b3a4b6593a4b6789e5c2b39694a3_u256, + 0xbe54820a45bf5a48381495b494e4a1f8e9a293b548394a5678d4b1a28583a2_u256, + FieldPrime - 1, + }) + { + const auto a = m.to_mont(t); + const auto a2 = m.mul(a, a); + const auto a2_sqrt = field_sqrt(m, a2); + ASSERT_TRUE(a2_sqrt.has_value()) << to_string(t); + EXPECT_TRUE(a2_sqrt == a || a2_sqrt == m.sub(0, a)) << to_string(t); + } +} + +TEST(secp256k1, field_sqrt_invalid) +{ + const evmmax::ModArith m{FieldPrime}; + + for (const auto& t : std::array{3_u256, FieldPrime - 1}) + { + EXPECT_FALSE(field_sqrt(m, m.to_mont(t)).has_value()); + } +} + +TEST(secp256k1, scalar_inv) +{ + const evmmax::ModArith n{Order}; + + for (const auto& t : std::array{ + 1_u256, + 0x6e140df17432311190232a91a38daed3ee9ed7f038645dd0278da7ca6e497de_u256, + Order - 1, + }) + { + ASSERT_LT(t, Order); + const auto a = n.to_mont(t); + const auto a_inv = scalar_inv(n, a); + const auto p = n.mul(a, a_inv); + EXPECT_EQ(n.from_mont(p), 1) << hex(t); + } +} + +TEST(secp256k1, calculate_y) +{ + const evmmax::ModArith m{FieldPrime}; + + struct TestCase + { + uint256 x; + uint256 y_even; + uint256 y_odd; + }; + + const TestCase test_cases[] = { + { + 1_u256, + 0x4218f20ae6c646b363db68605822fb14264ca8d2587fdd6fbc750d587e76a7ee_u256, + 0xbde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441_u256, + }, + { + 0xb697546bfbc062d06df1d25a26e4fadfe2f2a48109c349bf65d2b01182f3aa60_u256, + 0xd02714d31d0c08c38037400d232886863b473a37adba9823ea44ae50028a5bea_u256, + 0x2fd8eb2ce2f3f73c7fc8bff2dcd77979c4b8c5c8524567dc15bb51aefd75a045_u256, + }, + { + 0x18f4057699e2d9679421de8f4e11d7df9fa4b9e7cb841ea48aed75f1567b9731_u256, + 0x6db5b7ecd8e226c06f538d15173267bf1e78acc02bb856e83b3d6daec6a68144_u256, + 0x924a4813271dd93f90ac72eae8cd9840e187533fd447a917c4c2925039597aeb_u256, + }, + }; + + for (const auto& t : test_cases) + { + const auto x = m.to_mont(t.x); + + const auto y_even = calculate_y(m, x, false); + ASSERT_TRUE(y_even.has_value()); + EXPECT_EQ(m.from_mont(*y_even), t.y_even); + + const auto y_odd = calculate_y(m, x, true); + ASSERT_TRUE(y_odd.has_value()); + EXPECT_EQ(m.from_mont(*y_odd), t.y_odd); + } +} + +TEST(secp256k1, calculate_y_invalid) +{ + const evmmax::ModArith m{FieldPrime}; + + for (const auto& t : std::array{ + 0x207ea538f1835f6de40c793fc23d22b14da5a80015a0fecddf56f146b21d7949_u256, + FieldPrime - 1, + }) + { + const auto x = m.to_mont(t); + + const auto y_even = calculate_y(m, x, false); + ASSERT_FALSE(y_even.has_value()); + + const auto y_odd = calculate_y(m, x, true); + ASSERT_FALSE(y_odd.has_value()); + } +} + +TEST(secp256k1, point_to_address) +{ + // Check if converting the point at infinity gives the known address. + // https://www.google.com/search?q=0x3f17f1962B36e491b30A40b2405849e597Ba5FB5 + // https://etherscan.io/address/0x3f17f1962b36e491b30a40b2405849e597ba5fb5 + EXPECT_EQ(to_address(Point{}), 0x3f17f1962B36e491b30A40b2405849e597Ba5FB5_address); +} + +TEST(evmmax, secp256k1_calculate_u1) +{ + // u1 = -zr^(-1) + const auto z = 0x31d6fb860f6d12cee6e5b640646089bd5883d586e43de3dedc75695c11ac2da9_u256; + const auto r = 0x71cd6bfc24665312ff489aba9279710a560eda74aca333bf298785dc3cd72f6e_u256; + const auto expected = 0xd80ea4db5200c96e969270ab7c105e16abb9fc18a6e01cc99575dd3f5ce41eed_u256; + + const evmmax::ModArith m{evmmax::secp256k1::FieldPrime}; + const auto z_mont = m.to_mont(z); + const auto r_mont = m.to_mont(r); + const auto r_inv = field_inv(m, r_mont); + const auto z_neg = m.sub(0, z_mont); + const auto u1_mont = m.mul(z_neg, r_inv); + const auto u1 = m.from_mont(u1_mont); + EXPECT_EQ(u1, expected); +} + +TEST(evmmax, secp256k1_calculate_u2) +{ + // u2 = sr^(-1) + const auto r = 0x27bc00995393e969525f2d02e731437402aa12a9a09125d1e322d62f05a2b54f_u256; + const auto s = 0x7ce91fc325f28e78a016fa674a80d85581cc278d15453ea2fede2471b1adaada_u256; + const auto expected = 0xf888ea06899abc190fa37a165c98e6d4b00b13c50db1d1c34f38f0ab8fd9c29b_u256; + + const evmmax::ModArith m{evmmax::secp256k1::FieldPrime}; + const auto s_mont = m.to_mont(s); + const auto r_mont = m.to_mont(r); + const auto r_inv = field_inv(m, r_mont); + const auto u2_mont = m.mul(s_mont, r_inv); + const auto u2 = m.from_mont(u2_mont); + EXPECT_EQ(u2, expected); +} + +TEST(evmmax, secp256k1_hash_to_number) +{ + const auto max_h = ~uint256{}; + const auto hm = max_h % evmmax::secp256k1::FieldPrime; + + // Optimized mod. + const auto hm2 = max_h - evmmax::secp256k1::FieldPrime; + EXPECT_EQ(hm2, hm); +} + +TEST(evmmax, secp256k1_pt_add_inf) +{ + const Point p1{0x18f4057699e2d9679421de8f4e11d7df9fa4b9e7cb841ea48aed75f1567b9731_u256, + 0x6db5b7ecd8e226c06f538d15173267bf1e78acc02bb856e83b3d6daec6a68144_u256}; + const Point inf; + ASSERT_TRUE(inf.is_inf()); + + EXPECT_EQ(add(p1, inf), p1); + EXPECT_EQ(add(inf, p1), p1); + EXPECT_EQ(add(inf, inf), inf); +} + +TEST(evmmax, secp256k1_pt_add) +{ + const Point p1{0x18f4057699e2d9679421de8f4e11d7df9fa4b9e7cb841ea48aed75f1567b9731_u256, + 0x6db5b7ecd8e226c06f538d15173267bf1e78acc02bb856e83b3d6daec6a68144_u256}; + const Point p2{0xf929e07c83d65da3569113ae03998d13359ba982216285a686f4d66e721a0beb_u256, + 0xb6d73966107b10526e2e140c17f343ee0a373351f2b1408923151b027f55b82_u256}; + const Point p3{0xf929e07c83d65da3569113ae03998d13359ba982216285a686f4d66e721a0beb_u256, + 0xf4928c699ef84efad91d1ebf3e80cbc11f5c8ccae0d4ebf76dceae4ed80aa0ad_u256}; + const Point p4{ + 0x1_u256, 0xbde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441_u256}; + + { + const Point e = {0x40468d7704db3d11961ab9c222e35919d7e5d1baef59e0f46255d66bec3bd1d3_u256, + 0x6fff88d9f575236b6cc5c74e7d074832a460c2792fba888aea7b9986429dd7f7_u256}; + EXPECT_EQ(add(p1, p2), e); + } + { + const Point e = {0xd8e7b42b8c82e185bf0669ce0754697a6eb46c156497d5d1971bd6a23f38ed9e_u256, + 0x628c3107fc73c92e7b8c534e239257fb2de95bd6b965dc1021f636da086a7e99_u256}; + EXPECT_EQ(add(p1, p1), e); + } + { + const Point e = {0xdf592d726f42759020da10d3106db3880e514c783d6970d2a9085fb16879b37f_u256, + 0x10aa0ef9fe224e3797792b4b286b9f63542d4c11fe26d449a845b9db0f5993f9_u256}; + EXPECT_EQ(add(p1, p3), e); + } + { + const Point e = {0x12a5fd099bcd30e7290e58d63f8d5008287239500e6d0108020040497c5cb9c9_u256, + 0x7f6bd83b5ac46e3b59e24af3bc9bfbb213ed13e21d754e4950ae635961742574_u256}; + EXPECT_EQ(add(p1, p4), e); + } +} + +TEST(evmmax, secp256k1_pt_mul_inf) +{ + const Point p1{0x18f4057699e2d9679421de8f4e11d7df9fa4b9e7cb841ea48aed75f1567b9731_u256, + 0x6db5b7ecd8e226c06f538d15173267bf1e78acc02bb856e83b3d6daec6a68144_u256}; + const Point inf; + ASSERT_TRUE(inf.is_inf()); + + EXPECT_EQ(mul(p1, 0), inf); + EXPECT_EQ(mul(p1, evmmax::secp256k1::Order), inf); + EXPECT_EQ(mul(inf, 0), inf); + EXPECT_EQ(mul(inf, 1), inf); + EXPECT_EQ(mul(inf, evmmax::secp256k1::Order - 1), inf); + EXPECT_EQ(mul(inf, evmmax::secp256k1::Order), inf); +} + +TEST(evmmax, secp256k1_pt_mul) +{ + const Point p1{0x18f4057699e2d9679421de8f4e11d7df9fa4b9e7cb841ea48aed75f1567b9731_u256, + 0x6db5b7ecd8e226c06f538d15173267bf1e78acc02bb856e83b3d6daec6a68144_u256}; + + { + const auto d{100000000000000000000_u256}; + const Point e{0x4c34e6dc48badd579d1ce4702fd490fb98fa0e666417bfc2d4ff8e957d99c565_u256, + 0xb53da5be179d80c7f07226ba79b6bce643d89496b37d6bc2d111b009e37cc28b_u256}; + auto r = mul(p1, d); + EXPECT_EQ(r, e); + } + + { + const auto d{100000000000000000000000000000000_u256}; + const Point e{0xf86902594c8a4e4fc5f6dfb27886784271302c6bab3dc4350a0fe7c5b056af66_u256, + 0xb5748aa8f9122bfdcbf5846f6f8ec76f41626642a3f2ea0f483c92bf915847ad_u256}; + auto r = mul(p1, d); + EXPECT_EQ(r, e); + } + + { + const auto u1 = 0xd17a4c1f283fa5d67656ea81367b520eaa689207e5665620d4f51c7cf85fa220_u256; + const Point G{0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798_u256, + 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8_u256}; + const Point e{0x39cb41b2567f68137aae52e99dbe91cd38d9faa3ba6be536a04355b63a7964fe_u256, + 0xf31e6abd08cbd8e4896c9e0304b25000edcd52a9f6d2bac7cfbdad2c835c9a35_u256}; + auto r = mul(G, u1); + EXPECT_EQ(r, e); + } +} + + +struct TestCaseECR +{ + evmc::bytes32 hash; + uint256 r; + uint256 s; + bool parity = false; + Point pubkey; +}; + +static const TestCaseECR test_cases_ecr[] = { + {0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c_bytes32, + 0x7af9e73057870458f03c143483bc5fcb6f39d01c9b26d28ed9f3fe23714f6628_u256, + 0x3134a4ba8fafe11b351a720538398a5635e235c0b3258dce19942000731079ec_u256, false, + {0x43ec87f8ee6f58605d947dac51b5e4cfe26705f509e5dad058212aadda180835_u256, + 0x90ebad786ce091f5af1719bf30ee236a4e6ce8a7ab6c36a16c93c6177aa109df_u256}}, +}; + +TEST(evmmax, ecr) +{ + for (const auto& t : test_cases_ecr) + { + const auto h = std::bit_cast(t.hash); + const auto result = secp256k1_ecdsa_recover(h, t.r, t.s, t.parity); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->x, t.pubkey.x); + EXPECT_EQ(result->y, t.pubkey.y); + // EXPECT_EQ(*result, t.pubkey); + } +} + + +struct TestCaseECRecovery +{ + bytes input; + bytes expected_output; +}; + +static const TestCaseECRecovery test_cases[] = { + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"_hex, + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"_hex}, + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001b7af9e73057870458f03c143483bc5fcb6f39d01c9b26d28ed9f3fe23714f66283134a4ba8fafe11b351a720538398a5635e235c0b3258dce19942000731079ec"_hex, + "0000000000000000000000009a04aede774152f135315670f562c19c5726df2c"_hex}, + // z >= Order + {"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141000000000000000000000000000000000000000000000000000000000000001b7af9e73057870458f03c143483bc5fcb6f39d01c9b26d28ed9f3fe23714f66283134a4ba8fafe11b351a720538398a5635e235c0b3258dce19942000731079ec"_hex, + "000000000000000000000000b32CF3C8616537a28583FC00D29a3e8C9614cD61"_hex}, + {"6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9000000000000000000000000000000000000000000000000000000000000001b79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9"_hex, + {}}, + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"_hex, + {}}, + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f0000000000000000000000000000000000000000000000000000000000000000"_hex, + {}}, + // r >= Order + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001cfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"_hex, + {}}, + // s >= Order + {"18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"_hex, + {}}, +}; + +TEST(evmmax, ecrecovery) +{ + for (const auto& t : test_cases) + { + ASSERT_EQ(t.input.size(), 128); + + ethash::hash256 hash; + std::memcpy(hash.bytes, t.input.data(), 32); + const auto v{be::unsafe::load(&t.input[32])}; + ASSERT_TRUE(v == 27 || v == 28); + const auto r{be::unsafe::load(&t.input[64])}; + const auto s{be::unsafe::load(&t.input[96])}; + const bool parity = v == 28; + + const auto result = ecrecover(hash, r, s, parity); + + if (t.expected_output.empty()) + { + EXPECT_FALSE(result.has_value()); + } + else + { + ASSERT_EQ(t.expected_output.size(), 32); + evmc::address e; + memcpy(&e.bytes[0], &t.expected_output[12], 20); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, e); + } + } +} diff --git a/test/unittests/evmmax_test.cpp b/test/unittests/evmmax_test.cpp new file mode 100644 index 00000000..e078623c --- /dev/null +++ b/test/unittests/evmmax_test.cpp @@ -0,0 +1,149 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace intx; +using namespace evmmax; + +constexpr auto P23 = 23_u256; +constexpr auto BN254Mod = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256; +constexpr auto Secp256k1Mod = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_u256; +constexpr auto M256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_u256; +constexpr auto BLS12384Mod = + 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab_u384; + + +template +struct ModA : ModArith +{ + using uint = UintT; + ModA() : ModArith{Mod} {} +}; + +template +class evmmax_test : public testing::Test +{}; + +using test_types = testing::Types, ModA, + ModA, ModA, ModA>; +TYPED_TEST_SUITE(evmmax_test, test_types, testing::internal::DefaultNameGenerator); + +TYPED_TEST(evmmax_test, to_from_mont) +{ + const typename TypeParam::uint v = 1; + + const TypeParam s; + const auto x = s.to_mont(v); + EXPECT_EQ(s.from_mont(x), v); +} + +TYPED_TEST(evmmax_test, to_from_mont_0) +{ + const TypeParam s; + EXPECT_EQ(s.to_mont(0), 0); + EXPECT_EQ(s.from_mont(0), 0); +} + +template +static auto get_test_values(const Mod& m) noexcept +{ + using Uint = typename Mod::uint; + return std::array{ + m.mod - 1, + m.mod - 2, + m.mod / 2 + 1, + m.mod / 2, + m.mod / 2 - 1, + Uint{2}, + Uint{1}, + Uint{0}, + }; +} + +[[maybe_unused]] static void constexpr_test() +{ + // Make sure ModArith works in constexpr. + static constexpr ModArith m{BN254Mod}; + static_assert(m.mod == BN254Mod); + + static constexpr auto a = m.to_mont(3); + static constexpr auto b = m.to_mont(11); + static_assert(m.add(a, b) == m.to_mont(14)); + static_assert(m.sub(a, b) == m.to_mont(BN254Mod - 8)); + static_assert(m.mul(a, b) == m.to_mont(33)); +} + +TYPED_TEST(evmmax_test, add) +{ + const TypeParam m; + const auto values = get_test_values(m); + + for (const auto& x : values) + { + const auto xm = m.to_mont(x); + for (const auto& y : values) + { + const auto expected = + udivrem(intx::uint{x} + y, m.mod).rem; + + const auto ym = m.to_mont(y); + const auto s1m = m.add(xm, ym); + const auto s1 = m.from_mont(s1m); + EXPECT_EQ(s1, expected); + + // Conversion to Montgomery form is not necessary for addition to work. + const auto s2 = m.add(x, y); + EXPECT_EQ(s2, expected); + } + } +} + +TYPED_TEST(evmmax_test, sub) +{ + const TypeParam m; + const auto values = get_test_values(m); + + for (const auto& x : values) + { + const auto xm = m.to_mont(x); + for (const auto& y : values) + { + const auto expected = + udivrem(intx::uint{x} + m.mod - y, m.mod).rem; + + const auto ym = m.to_mont(y); + const auto d1m = m.sub(xm, ym); + const auto d1 = m.from_mont(d1m); + EXPECT_EQ(d1, expected); + + // Conversion to Montgomery form is not necessary for subtraction to work. + const auto d2 = m.sub(x, y); + EXPECT_EQ(d2, expected); + } + } +} + +TYPED_TEST(evmmax_test, mul) +{ + const TypeParam m; + const auto values = get_test_values(m); + + for (const auto& x : values) + { + const auto xm = m.to_mont(x); + for (const auto& y : values) + { + const auto expected = udivrem(umul(x, y), m.mod).rem; + + const auto ym = m.to_mont(y); + const auto pm = m.mul(xm, ym); + const auto p = m.from_mont(pm); + EXPECT_EQ(p, expected); + } + } +} diff --git a/test/unittests/execution_state_test.cpp b/test/unittests/execution_state_test.cpp index 02343961..64ca5b6e 100644 --- a/test/unittests/execution_state_test.cpp +++ b/test/unittests/execution_state_test.cpp @@ -7,17 +7,17 @@ #include #include -static_assert(std::is_default_constructible::value); -static_assert(!std::is_move_constructible::value); -static_assert(!std::is_copy_constructible::value); -static_assert(!std::is_move_assignable::value); -static_assert(!std::is_copy_assignable::value); - -static_assert(std::is_default_constructible::value); -static_assert(!std::is_move_constructible::value); -static_assert(!std::is_copy_constructible::value); -static_assert(!std::is_move_assignable::value); -static_assert(!std::is_copy_assignable::value); +static_assert(std::is_default_constructible_v); +static_assert(std::is_move_constructible_v); +static_assert(!std::is_copy_constructible_v); +static_assert(std::is_move_assignable_v); +static_assert(!std::is_copy_assignable_v); + +static_assert(std::is_default_constructible_v); +static_assert(std::is_move_constructible_v); +static_assert(!std::is_copy_constructible_v); +static_assert(std::is_move_assignable_v); +static_assert(!std::is_copy_assignable_v); TEST(execution_state, construct) { @@ -39,7 +39,7 @@ TEST(execution_state, construct) TEST(execution_state, default_construct) { - const evmone::ExecutionState st{}; + const evmone::ExecutionState st; EXPECT_EQ(st.memory.size(), 0); EXPECT_EQ(st.msg, nullptr); @@ -52,10 +52,10 @@ TEST(execution_state, default_construct) TEST(execution_state, default_construct_advanced) { - const evmone::advanced::AdvancedExecutionState st; + evmone::advanced::AdvancedExecutionState st; EXPECT_EQ(st.gas_left, 0); - EXPECT_EQ(st.stack.size(), 0); + EXPECT_EQ(st.stack_size(), 0); EXPECT_EQ(st.memory.size(), 0); EXPECT_EQ(st.msg, nullptr); EXPECT_EQ(st.rev, EVMC_FRONTIER); @@ -90,7 +90,7 @@ TEST(execution_state, reset_advanced) EXPECT_EQ(st.gas_left, 1); EXPECT_EQ(st.gas_state.cpu_gas_refund(), 2); - EXPECT_EQ(st.stack.size(), 1); + EXPECT_EQ(st.stack_size(), 1); EXPECT_EQ(st.memory.size(), 64); EXPECT_EQ(st.msg, &msg); EXPECT_EQ(st.rev, EVMC_BYZANTIUM); @@ -113,7 +113,7 @@ TEST(execution_state, reset_advanced) // test. EXPECT_EQ(st.gas_left, 13); EXPECT_EQ(st.gas_state.cpu_gas_refund(), 0); - EXPECT_EQ(st.stack.size(), 0); + EXPECT_EQ(st.stack_size(), 0); EXPECT_EQ(st.memory.size(), 0); EXPECT_EQ(st.msg, &msg2); EXPECT_EQ(st.rev, EVMC_HOMESTEAD); @@ -126,35 +126,6 @@ TEST(execution_state, reset_advanced) } } -TEST(execution_state, stack_reset) -{ - evmone::StackSpace stack_space; - evmone::advanced::Stack stack{stack_space.bottom()}; - EXPECT_EQ(stack.size(), 0); - - stack.push({}); - EXPECT_EQ(stack.size(), 1); - - stack.reset(stack_space.bottom()); - EXPECT_EQ(stack.size(), 0); - - stack.reset(stack_space.bottom()); - EXPECT_EQ(stack.size(), 0); -} - -TEST(execution_state, const_stack) -{ - evmone::StackSpace stack_space; - evmone::advanced::Stack stack{stack_space.bottom()}; - stack.push(1); - stack.push(2); - - const auto& cstack = stack; - - EXPECT_EQ(cstack[0], 2); - EXPECT_EQ(cstack[1], 1); -} - TEST(execution_state, memory_view) { evmone::Memory memory; diff --git a/test/unittests/exportable_fixture.cpp b/test/unittests/exportable_fixture.cpp new file mode 100644 index 00000000..316189a5 --- /dev/null +++ b/test/unittests/exportable_fixture.cpp @@ -0,0 +1,46 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#ifdef _MSC_VER +// Disable warning C4996: 'getenv': This function or variable may be unsafe. +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "exportable_fixture.hpp" +#include +#include + +namespace fs = std::filesystem; + +namespace evmone::test +{ +namespace +{ +/// Creates the file path for the exported test based on its name. +fs::path get_export_test_path(const testing::TestInfo& test_info, std::string_view export_dir) +{ + const std::string suite_name{test_info.test_suite_name()}; + + const auto stem = fs::path{test_info.file()}.stem().string(); + const std::regex re{suite_name + "_(.*)_test"}; + std::smatch m; + const auto sub_suite_name = std::regex_match(stem, m, re) ? m[1] : std::string{}; + + const auto dir = fs::path{export_dir} / suite_name / sub_suite_name; + + fs::create_directories(dir); + return dir / (std::string{test_info.name()} + ".json"); +} +} // namespace + +ExportableFixture::ExportableFixture() +{ + if (const auto export_dir = std::getenv("EVMONE_EXPORT_TESTS"); export_dir != nullptr) + { + const auto& test_info = *testing::UnitTest::GetInstance()->current_test_info(); + export_test_name = test_info.name(); + export_file_path = get_export_test_path(test_info, export_dir).string(); + } +} +} // namespace evmone::test diff --git a/test/unittests/exportable_fixture.hpp b/test/unittests/exportable_fixture.hpp new file mode 100644 index 00000000..892c544e --- /dev/null +++ b/test/unittests/exportable_fixture.hpp @@ -0,0 +1,18 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone::test +{ +class ExportableFixture : public testing::Test +{ +protected: + std::string_view export_test_name; + std::string export_file_path; + + ExportableFixture(); +}; +} // namespace evmone::test diff --git a/test/unittests/instructions_test.cpp b/test/unittests/instructions_test.cpp index f7f1b136..b31f8184 100644 --- a/test/unittests/instructions_test.cpp +++ b/test/unittests/instructions_test.cpp @@ -22,7 +22,7 @@ namespace { constexpr int unspecified = -1000000; -constexpr int get_revision_defined_in(size_t op) noexcept +constexpr int get_revision_defined_in(uint8_t op) noexcept { for (size_t r = EVMC_FRONTIER; r <= EVMC_MAX_REVISION; ++r) { @@ -32,13 +32,15 @@ constexpr int get_revision_defined_in(size_t op) noexcept return unspecified; } -constexpr bool is_terminating(Opcode op) noexcept +constexpr bool is_terminating(uint8_t op) noexcept { switch (op) { case OP_STOP: case OP_RETURN: case OP_RETF: + case OP_JUMPF: + case OP_RETURNCONTRACT: case OP_REVERT: case OP_INVALID: case OP_SELFDESTRUCT: @@ -48,7 +50,7 @@ constexpr bool is_terminating(Opcode op) noexcept } } -template +template constexpr void validate_traits_of() noexcept { constexpr auto tr = instr::traits[Op]; @@ -56,10 +58,15 @@ constexpr void validate_traits_of() noexcept // immediate_size if constexpr (Op >= OP_PUSH1 && Op <= OP_PUSH32) static_assert(tr.immediate_size == Op - OP_PUSH1 + 1); - else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF) + else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF || Op == OP_JUMPF) static_assert(tr.immediate_size == 2); - else if constexpr (Op == OP_DUPN || Op == OP_SWAPN) + else if constexpr (Op == OP_RJUMPV) static_assert(tr.immediate_size == 1); + else if constexpr (Op == OP_DUPN || Op == OP_SWAPN || Op == OP_EXCHANGE || Op == OP_EOFCREATE || + Op == OP_RETURNCONTRACT) + static_assert(tr.immediate_size == 1); + else if constexpr (Op == OP_DATALOADN) + static_assert(tr.immediate_size == 2); else static_assert(tr.immediate_size == 0); // Including RJUMPV. @@ -68,7 +75,11 @@ constexpr void validate_traits_of() noexcept // since constexpr auto expected_rev = get_revision_defined_in(Op); - static_assert(tr.since.has_value() ? *tr.since == expected_rev : expected_rev == unspecified); + constexpr auto since = + tr.since.has_value() ? + tr.eof_since.has_value() ? std::min(*tr.since, *tr.eof_since) : *tr.since : + tr.eof_since; + static_assert(since.has_value() ? *since == expected_rev : expected_rev == unspecified); } template @@ -103,13 +114,30 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept switch (op) { + case OP_BLOBHASH: + case OP_BLOBBASEFEE: case OP_RJUMP: case OP_RJUMPI: case OP_RJUMPV: case OP_CALLF: case OP_RETF: + case OP_JUMPF: case OP_DUPN: case OP_SWAPN: + case OP_EXCHANGE: + case OP_MCOPY: + case OP_DATALOAD: + case OP_DATALOADN: + case OP_DATASIZE: + case OP_DATACOPY: + case OP_RETURNDATALOAD: + case OP_TLOAD: + case OP_TSTORE: + case OP_EXTCALL: + case OP_EXTDELEGATECALL: + case OP_EXTSTATICCALL: + case OP_EOFCREATE: + case OP_RETURNCONTRACT: return true; default: return false; diff --git a/test/unittests/precompiles_blake2b_test.cpp b/test/unittests/precompiles_blake2b_test.cpp new file mode 100644 index 00000000..67c19f0d --- /dev/null +++ b/test/unittests/precompiles_blake2b_test.cpp @@ -0,0 +1,81 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +using evmone::crypto::blake2b_compress; + +// Initialization Vector. +// https://datatracker.ietf.org/doc/html/rfc7693#appendix-C.2 +constexpr std::array blake2b_iv{ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, +}; + +TEST(blake2b_compress, reference_test) +{ + // The reference test from the RFC. + // https://datatracker.ietf.org/doc/html/rfc7693#appendix-A + // with some extensions by modifying the "rounds" and "last" values. + + using evmone::test::hex; + + auto h_init = blake2b_iv; + h_init[0] ^= 0x01010000 ^ /*outlen = */ 64; + + const std::string_view data = "abc"; + uint64_t m[16]{}; + std::memcpy(m, data.data(), data.size()); + + const uint64_t t[2]{data.size(), 0}; + + auto h = h_init; + blake2b_compress(12, h.data(), m, t, true); + EXPECT_EQ(hex({reinterpret_cast(h.data()), sizeof(h)}), + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); + + // https://github.com/ethereum/tests/blob/v13.2/src/GeneralStateTestsFiller/stPreCompiledContracts/blake2BFiller.yml#L301-L302 + h = h_init; + blake2b_compress(12, h.data(), m, t, false); + EXPECT_EQ(hex({reinterpret_cast(h.data()), sizeof(h)}), + "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d28752" + "98743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); + + // https://github.com/ethereum/tests/blob/v13.2/src/GeneralStateTestsFiller/stPreCompiledContracts/blake2BFiller.yml#L268-L269 + h = h_init; + blake2b_compress(0, h.data(), m, t, true); + EXPECT_EQ(hex({reinterpret_cast(h.data()), sizeof(h)}), + "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5" + "d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + + // this gives the same result because the xor zeros out the "last" flag + h = h_init; + blake2b_compress(0, h.data(), m, t, false); + EXPECT_EQ(hex({reinterpret_cast(h.data()), sizeof(h)}), + "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5" + "d282e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"); +} + +TEST(blake2b_compress, null_input) +{ + std::array h{}; + const uint64_t t[2]{}; + + // the data block is unused so be pass nullptr. + blake2b_compress(0, h.data(), nullptr, t, false); + + // For null input you get the IV as the result. + EXPECT_EQ(h, blake2b_iv); +} diff --git a/test/unittests/precompiles_bls_test.cpp b/test/unittests/precompiles_bls_test.cpp new file mode 100644 index 00000000..228428ed --- /dev/null +++ b/test/unittests/precompiles_bls_test.cpp @@ -0,0 +1,310 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +using evmone::test::operator""_hex; + +TEST(bls, g1_add) +{ + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + + const auto expected_x = + "000000000000000000000000000000000a40300ce2dec9888b60690e9a41d3004fda4886854573974fab73b046d3147ba5b7a5bde85279ffede1b45b3918d82d"_hex; + const auto expected_y = + "0000000000000000000000000000000006d3d887e9f53b9ec4eb6cedf5607226754b07c01ace7834f57f3e7315faefb739e59018e22c492006190fba4a870025"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_add_not_on_curve) +{ + { + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6ba"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_FALSE( + evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + } + { + const auto x0 = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y0 = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto x1 = + "00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca9426"_hex; + const auto y1 = + "00000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a22"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_FALSE( + evmone::crypto::bls::g1_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + } +} + +TEST(bls, g1_mul) +{ + const auto x = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"_hex; + const auto y = + "0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"_hex; + const auto c = "0000000000000000000000000000000000000000000000000000000000000002"_hex; + + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_mul(rx, ry, x.data(), y.data(), c.data())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_add) +{ + const auto x0 = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e"_hex; + const auto y0 = + "000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be"_hex; + const auto x1 = + "00000000000000000000000000000000103121a2ceaae586d240843a398967325f8eb5a93e8fea99b62b9f88d8556c80dd726a4b30e84a36eeabaf3592937f2700000000000000000000000000000000086b990f3da2aeac0a36143b7d7c824428215140db1bb859338764cb58458f081d92664f9053b50b3fbd2e4723121b68"_hex; + const auto y1 = + "000000000000000000000000000000000f9e7ba9a86a8f7624aa2b42dcc8772e1af4ae115685e60abc2c9b90242167acef3d0be4050bf935eed7c3b6fc7ba77e000000000000000000000000000000000d22c3652d0dc6f0fc9316e14268477c2049ef772e852108d269d9c38dba1d4802e8dae479818184c08f9a569d878451"_hex; + + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_add(rx, ry, x0.data(), y0.data(), x1.data(), y1.data())); + + const auto expected_x = + "000000000000000000000000000000000b54a8a7b08bd6827ed9a797de216b8c9057b3a9ca93e2f88e7f04f19accc42da90d883632b9ca4dc38d013f71ede4db00000000000000000000000000000000077eba4eecf0bd764dce8ed5f45040dd8f3b3427cb35230509482c14651713282946306247866dfe39a8e33016fcbe52"_hex; + const auto expected_y = + "0000000000000000000000000000000014e60a76a29ef85cbd69f251b9f29147b67cfe3ed2823d3f9776b3a0efd2731941d47436dc6d2b58d9e65f8438bad073000000000000000000000000000000001586c3c910d95754fef7a732df78e279c3d37431c6a2b77e67a00c7c130a8fcd4d19f159cbeb997a178108fffffcbd20"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_mul) +{ + const auto x = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e"_hex; + const auto y = + "000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be"_hex; + const auto c = "0000000000000000000000000000000000000000000000000000000000000002"_hex; + + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_mul(rx, ry, x.data(), y.data(), c.data())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_msm) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f17" + "1bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e" + "30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000" + "00000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_msm_inf_0) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e10000000000000000000000000000000000000000000000000000000000000000"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g1_msm_inf_2) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::g1_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000000572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e"_hex; + const auto expected_y = + "00000000000000000000000000000000166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm_inf_0) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000000"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, g2_msm_inf_2) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::g2_msm(rx, ry, input.data(), input.size())); + + const auto expected_x = + "000000000000000000000000000000001638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053000000000000000000000000000000000a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577"_hex; + const auto expected_y = + "000000000000000000000000000000000468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899000000000000000000000000000000000f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, map_fp_to_g1) +{ + using namespace evmc::literals; + auto input = + "00000000000000000000000000000000156c8a6a2c184569d69a76be144b5cdc5141d2d2ca4fe341f011e25e3969c55ad9e9b9ce2eb833c81a908e5fa4ac5f03"_hex; + uint8_t rx[64]; + uint8_t ry[64]; + + EXPECT_TRUE(evmone::crypto::bls::map_fp_to_g1(rx, ry, input.data())); + + const auto expected_x = + "00000000000000000000000000000000184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba"_hex; + const auto expected_y = + "0000000000000000000000000000000004407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, map_fp2_to_g2) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000007355d25caf6e7f2f0cb2812ca0e513bd026ed09dda65b177500fa31714e09ea0ded3a078b526bed3307f804d4b93b040000000000000000000000000000000002829ce3c021339ccb5caf3e187f6370e1e2a311dec9b75363117063ab2015603ff52c3d3b98f19c2f65575e99e8b78c"_hex; + uint8_t rx[128]; + uint8_t ry[128]; + + EXPECT_TRUE(evmone::crypto::bls::map_fp2_to_g2(rx, ry, input.data())); + + const auto expected_x = + "0000000000000000000000000000000000e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb700000000000000000000000000000000126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b"_hex; + const auto expected_y = + "000000000000000000000000000000000caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42000000000000000000000000000000001498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d"_hex; + + EXPECT_EQ(evmc::bytes_view(rx, sizeof rx), expected_x); + EXPECT_EQ(evmc::bytes_view(ry, sizeof ry), expected_y); +} + +TEST(bls, paring_check) +{ + using namespace evmc::literals; + auto input = + "0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be00000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca942600000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a2100000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be000000000000000000000000000000000a40300ce2dec9888b60690e9a41d3004fda4886854573974fab73b046d3147ba5b7a5bde85279ffede1b45b3918d82d0000000000000000000000000000000006d3d887e9f53b9ec4eb6cedf5607226754b07c01ace7834f57f3e7315faefb739e59018e22c492006190fba4a87002500000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000d1b3cc2c7027888be51d9ef691d77bcb679afda66c73f17f9ee3837a55024f78c71363275a75d75d86bab79f74782aa0000000000000000000000000000000013fa4d4a0ad8b1ce186ed5061789213d993923066dddaf1040bc3ff59f825c78df74f2d75467e25e0f55f8a00fa030ed"_hex; + uint8_t r[32]; + + EXPECT_TRUE(evmone::crypto::bls::pairing_check(r, input.data(), input.size())); + + const auto expected = "0000000000000000000000000000000000000000000000000000000000000001"_hex; + + EXPECT_EQ(evmc::bytes_view(r, sizeof r), expected); +} diff --git a/test/unittests/precompiles_kzg_test.cpp b/test/unittests/precompiles_kzg_test.cpp new file mode 100644 index 00000000..9437379d --- /dev/null +++ b/test/unittests/precompiles_kzg_test.cpp @@ -0,0 +1,69 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include + +using namespace evmc::literals; +using namespace evmone::crypto; + +namespace +{ +// TODO(intx): Add ""_u384. +consteval auto operator""_u384(const char* s) +{ + return intx::from_string(s); +} + +constexpr auto G1_GENERATOR_X = + 0x17F1D3A73197D7942695638C4FA9AC0FC3688C4F9774B905A14E3A3F171BAC586C55E83FF97A1AEFFB3AF00ADB22C6BB_u384; +constexpr std::byte ZERO32[32]{}; +constexpr std::byte POINT_AT_INFINITY[48]{std::byte{0xC0}}; + +auto versioned_hash(std::span input) noexcept +{ + std::array hash{}; + sha256(hash.data(), input.data(), input.size()); + hash[0] = VERSIONED_HASH_VERSION_KZG; + return hash; +} +} // namespace + +TEST(kzg, verify_proof_hash_invalid) +{ + const auto r = kzg_verify_proof(ZERO32, ZERO32, ZERO32, POINT_AT_INFINITY, POINT_AT_INFINITY); + EXPECT_FALSE(r); +} + +TEST(kzg, verify_proof_zero) +{ + // Commit and prove polynomial f(x) = 0. + std::byte z[32]{}; + z[13] = std::byte{17}; // can be any value because f(z) is always 0. + const auto hash = versioned_hash(POINT_AT_INFINITY); + const auto r = kzg_verify_proof(hash.data(), z, ZERO32, POINT_AT_INFINITY, POINT_AT_INFINITY); + EXPECT_TRUE(r); +} + +TEST(kzg, verify_proof_constant) +{ + // Commit and prove polynomial f(x) = 1. + std::byte z[32]{}; + z[13] = std::byte{17}; // can be any value because f(z) is always 0. + std::byte y[32]{}; + y[31] = std::byte{1}; + + // Commitment for f(x) = 1 is [1]₁, i.e. the G1 generator point. + std::byte c[48]{}; + intx::be::store(reinterpret_cast(c), G1_GENERATOR_X); + c[0] |= std::byte{0x80}; // flag of the point compressed form. + + const auto hash = versioned_hash(c); + const auto r = kzg_verify_proof(hash.data(), z, y, c, POINT_AT_INFINITY); + EXPECT_TRUE(r); +} diff --git a/test/unittests/precompiles_ripemd160_test.cpp b/test/unittests/precompiles_ripemd160_test.cpp new file mode 100644 index 00000000..5a98b811 --- /dev/null +++ b/test/unittests/precompiles_ripemd160_test.cpp @@ -0,0 +1,71 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +using evmone::crypto::ripemd160; + +inline std::string hex(std::span x) +{ + return evmc::hex({reinterpret_cast(x.data()), x.size()}); +} + +TEST(ripemd160, test_vectors) +{ + // Some test vectors from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html. + + const std::pair test_cases[] = { + {"abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"}, + {"message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"}, + {"abcdefghijklmnopqrstuvwxyz", "f71c27109c692c1b56bbdceb5b9d2865b3708dbc"}, + {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "12a053384a9c0c88e405a06c27dcf49ada62eb2b"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "b0e20b6e3116640286ed3a87a5713079b21f5189"}, + {"12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "9b752e45573d4b39f4dbd3323cab82bf63326bfb"}, + {"The quick brown fox jumps over the lazy dog", "37f332f68db77bd9d7edd4969571ad671cf9dd3b"}, + }; + + for (const auto& [input, hash_hex] : test_cases) + { + std::byte hash[20]; + ripemd160(hash, reinterpret_cast(input.data()), input.size()); + EXPECT_EQ(hex({hash, std::size(hash)}), hash_hex); + } +} + +TEST(ripemd160, input_length) +{ + const std::pair test_cases[] = { + {0, "9c1185a5c5e9fc54612808977ee8f548b2258d31"}, + {1, "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"}, + {54, "a57fa1577740fd73b6859dd20e090cdac4d2af36"}, + {55, "0d8a8c9063a48576a7c97e9f95253a6e53ff6765"}, // length fits into the first block + {56, "e72334b46c83cc70bef979e15453706c95b888be"}, + {57, "eed82d19d597ab275b550ff3d6e0bc2a75350388"}, + {63, "e640041293fe663b9bf3f8c21ffecac03819e6b2"}, + {64, "9dfb7d374ad924f3f88de96291c33e9abed53e32"}, // full block + {65, "99724bb11811e7166af38f671b6a082d8ab4960b"}, + {119, "23e398ff2bac815aa1bbb57ca2a669c841872919"}, + {120, "c476770a6dae31fcee8d25efe6559a05c8024595"}, + {121, "725c88a6f41605e99477a1478607d3fe25ced606"}, + {127, "64f2d68b85f394e2e4f49009c4bd50224c2698ed"}, + {128, "8dfdfb32b2ed5cb41a73478b4fd60cc5b4648b15"}, // two blocks + {129, "62bb9091f499f294f15aa5b951df4d9744d50cf2"}, + {1'000'000, "52783243c1697bdbe16d37f97f68f08325dc1528"}, + }; + + for (const auto& [input_length, hash_hex] : test_cases) + { + const auto input = std::make_unique_for_overwrite(input_length); + std::fill_n(input.get(), input_length, std::byte{'a'}); + std::byte hash[20]; + ripemd160(hash, input.get(), input_length); + EXPECT_EQ(hex({hash, std::size(hash)}), hash_hex) << input_length; + } +} diff --git a/test/unittests/precompiles_sha256_test.cpp b/test/unittests/precompiles_sha256_test.cpp new file mode 100644 index 00000000..9a3e078b --- /dev/null +++ b/test/unittests/precompiles_sha256_test.cpp @@ -0,0 +1,32 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using evmone::crypto::sha256; + +TEST(sha256, test_vectors) +{ + // Some test vectors from https://www.di-mgt.com.au/sha_testvectors.html. + + const std::pair test_cases[] = { + {"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {"abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}, + {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"}, + {"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"}, + }; + + for (const auto& [input, expected_hash_hex] : test_cases) + { + std::byte hash[evmone::crypto::SHA256_HASH_SIZE]; + sha256(hash, reinterpret_cast(input.data()), input.size()); + const auto hash_hex = evmc::hex({reinterpret_cast(hash), std::size(hash)}); + EXPECT_EQ(hash_hex, expected_hash_hex); + } +} diff --git a/test/unittests/state_block_test.cpp b/test/unittests/state_block_test.cpp new file mode 100644 index 00000000..8325763e --- /dev/null +++ b/test/unittests/state_block_test.cpp @@ -0,0 +1,27 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +using namespace evmone::state; +using namespace intx::literals; + +TEST(state_block, blob_gas_price) +{ + static constexpr uint64_t TARGET_BLOB_GAS_PER_BLOCK = 0x60000; + + EXPECT_EQ(compute_blob_gas_price(0), 1); + EXPECT_EQ(compute_blob_gas_price(1), 1); + EXPECT_EQ(compute_blob_gas_price(TARGET_BLOB_GAS_PER_BLOCK), 1); + EXPECT_EQ(compute_blob_gas_price(TARGET_BLOB_GAS_PER_BLOCK * 2), 1); + EXPECT_EQ(compute_blob_gas_price(TARGET_BLOB_GAS_PER_BLOCK * 7), 2); + + EXPECT_EQ(compute_blob_gas_price(10'000'000), 19); + EXPECT_EQ(compute_blob_gas_price(100'000'000), 10203769476395); + + // Close to the computation overflowing: + EXPECT_EQ(compute_blob_gas_price(400'000'000), + 10840331274704280429132033759016842817414750029778539_u256); +} diff --git a/test/unittests/state_bloom_filter_test.cpp b/test/unittests/state_bloom_filter_test.cpp index 60d97152..92f37f3d 100644 --- a/test/unittests/state_bloom_filter_test.cpp +++ b/test/unittests/state_bloom_filter_test.cpp @@ -2,26 +2,16 @@ // Copyright 2023 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 -#include "../utils/utils.hpp" #include #include #include #include +#include #include using namespace evmc::literals; using namespace evmone::state; - -namespace -{ -BloomFilter bloom_filter_from_bytes(const bytes_view& data) noexcept -{ - assert(data.size() == 256); - BloomFilter res; - std::copy(std::begin(data), std::end(data), std::begin(res.bytes)); - return res; -} -} // namespace +using namespace evmone::test; TEST(state_bloom_filter, combine_blooms) { diff --git a/test/unittests/state_difficulty_test.cpp b/test/unittests/state_difficulty_test.cpp new file mode 100644 index 00000000..e6613013 --- /dev/null +++ b/test/unittests/state_difficulty_test.cpp @@ -0,0 +1,176 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +using namespace evmone::state; + +struct DifficultyTest // NOLINT(clang-analyzer-optin.performance.Padding) +{ + evmc_revision rev; + const char* name; + int64_t block_number; + int64_t difficulty; + int64_t timestamp; + int64_t parent_difficulty; + int64_t parent_timestamp; + bool parent_has_ommers; +}; + +/// Example difficulty tests from +/// https://github.com/ethereum/tests/blob/develop/DifficultyTests. +static constexpr DifficultyTest tests[] = { + { + EVMC_FRONTIER, + "DifficultyTest1", + 0x0186a0, + 0x6a8d5758858f3fb6, + 0x75311a08b, + 0x6a8007579a9bec39, + 0x75311a08a, + false, + }, + { + EVMC_FRONTIER, + "DifficultyTest1040", + 0x10c8e0, + 0x3857fe2e57047922, + 0x3558049dc, + 0x385f0a0f98f79614, + 0x3558049c8, + true, + }, + { + EVMC_FRONTIER, + "DifficultyTest1031", + 0x030d40, + 0x7b435a6e9d83b81e, + 0x2442b7295, + 0x7b52c4c7366a856d, + 0x2442b7281, + true, + }, + { + EVMC_HOMESTEAD, + "DifficultyTest1", + 0x0186a0, + 0x6ab7534e3bcfec27, + 0x68450ae0d, + 0x6aa9fe0e7a00ac12, + 0x68450ae0c, + false, + }, + { + EVMC_HOMESTEAD, + "DifficultyTest1040", + 0x10c8e0, + 0x024bf6f60ecc847d, + 0x7b389fe96, + 0x024c407e1e905487, + 0x7b389fe82, + true, + }, + { + EVMC_HOMESTEAD, + "DifficultyTest1031", + 0x030d40, + 0x39699ae4587a0b05, + 0x3b907b4f4, + 0x3970c8fd78291026, + 0x3b907b4e0, + true, + }, + { + EVMC_BYZANTIUM, + "DifficultyTest1", + 0x0186a0, + 0x69702c7f2c9fad14, + 0x28d214819, + 0x6963001f28ba95c2, + 0x28d214818, + false, + }, + { + EVMC_BYZANTIUM, + "DifficultyTest1038", + 0x0dbba0, + 0x79f2cbb8c97579b0, + 0x72e440371, + 0x79f2cbb8c97579b0, + 0x72e44035d, + true, + }, + { + EVMC_BERLIN, + "DifficultyTest1", + 0x186a0, + 0x56c67d1e106966c3, + 0x63ed689e9, + 0x56bba5a95b3dff04, + 0x63ed689e8, + false, + }, + { + EVMC_BERLIN, + "DifficultyTest1040", + 0x10c8e0, + 0x68f7512123928555, + 0x617ec9fcc, + 0x68f7512123928555, + 0x617ec9fb8, + true, + }, + { + EVMC_FRONTIER, + "min_difficulty_frontier", + 1, + 0x20000, + 1100, + 0x20000, + 1000, + false, + }, + { + EVMC_HOMESTEAD, + "min_difficulty_homestead", + 1, + 0x20000, + 2000, + 0x21999, + 1000, + false, + }, + { + EVMC_BYZANTIUM, + "min_difficulty_byzantium", + 3'000'001, + 0x20000, + 10060, + 0x20139, + 10000, + false, + }, + { + // Calculated difficulty is exactly 0x20000 without min cap. + EVMC_BYZANTIUM, + "min_difficulty_byzantium2", + 3'000'001, + 0x20000, + 10060, + 0x20140, + 10000, + false, + }, +}; + +TEST(state_difficulty, tests) +{ + for (const auto& t : tests) + { + const auto difficulty = calculate_difficulty(t.parent_difficulty, t.parent_has_ommers, + t.parent_timestamp, t.timestamp, t.block_number, t.rev); + EXPECT_EQ(difficulty, t.difficulty) << t.rev << "/" << t.name; + } +} diff --git a/test/unittests/state_mpt_hash_test.cpp b/test/unittests/state_mpt_hash_test.cpp index f248920a..15048aa0 100644 --- a/test/unittests/state_mpt_hash_test.cpp +++ b/test/unittests/state_mpt_hash_test.cpp @@ -2,23 +2,25 @@ // Copyright 2022 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 -#include "../utils/utils.hpp" #include -#include #include #include #include #include #include +#include +#include #include +using namespace intx::literals; using namespace evmone; using namespace evmone::state; -using namespace intx; +using namespace evmone::test; + TEST(state_mpt_hash, empty) { - EXPECT_EQ(mpt_hash(std::unordered_map()), emptyMPTHash); + EXPECT_EQ(mpt_hash(TestState{}), EMPTY_MPT_HASH); } TEST(state_mpt_hash, single_account_v1) @@ -27,25 +29,21 @@ TEST(state_mpt_hash, single_account_v1) constexpr auto expected = 0x084f337237951e425716a04fb0aaa74111eda9d9c61767f2497697d0a201c92e_bytes32; - Account acc; - acc.balance = 1_u256; - const std::unordered_map accounts{{0x02_address, acc}}; + const TestState accounts{{0x02_address, {.balance = 1}}}; EXPECT_EQ(mpt_hash(accounts), expected); } TEST(state_mpt_hash, two_accounts) { - std::unordered_map accounts; - EXPECT_EQ(mpt_hash(accounts), emptyMPTHash); - - accounts[0x00_address] = Account{}; + TestState accounts; + accounts[0x00_address] = {}; EXPECT_EQ(mpt_hash(accounts), 0x0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785_bytes32); - Account acc2; + TestAccount acc2; acc2.nonce = 1; acc2.balance = -2_u256; - acc2.code = {0x00}; + acc2.code = bytes{0x00}; // Note: `= {0x00}` causes GCC 12 warning at -O3. acc2.storage[0x01_bytes32] = {0xfe_bytes32}; acc2.storage[0x02_bytes32] = {0xfd_bytes32}; accounts[0x01_address] = acc2; @@ -55,11 +53,11 @@ TEST(state_mpt_hash, two_accounts) TEST(state_mpt_hash, deleted_storage) { - Account acc; + TestAccount acc; acc.storage[0x01_bytes32] = {}; acc.storage[0x02_bytes32] = {0xfd_bytes32}; acc.storage[0x03_bytes32] = {}; - const std::unordered_map accounts{{0x07_address, acc}}; + const TestState accounts{{0x07_address, acc}}; EXPECT_EQ(mpt_hash(accounts), 0x4e7338c16731491e0fb5d1623f5265c17699c970c816bab71d4d717f6071414d_bytes32); } @@ -70,7 +68,7 @@ TEST(state_mpt_hash, one_transactions) Transaction tx{}; - tx.kind = Transaction::Kind::eip1559; + tx.type = Transaction::Type::eip1559; tx.data = "04a7e62e00000000000000000000000000000000000000000000000000000000000000c0000000000000000000" "000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000" @@ -186,9 +184,9 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs) //} TransactionReceipt receipt0{}; - receipt0.kind = evmone::state::Transaction::Kind::legacy; + receipt0.type = evmone::state::Transaction::Type::legacy; receipt0.status = EVMC_SUCCESS; - receipt0.gas_used = 0x24522; + receipt0.cumulative_gas_used = 0x24522; Log l0; l0.addr = 0x84bf5c35c54a994c72ff9d8b4cca8f5034153a2c_address; @@ -236,11 +234,72 @@ TEST(state_mpt_hash, legacy_and_eip1559_receipt_three_logs_no_logs) //} TransactionReceipt receipt1{}; - receipt1.kind = evmone::state::Transaction::Kind::eip1559; + receipt1.type = evmone::state::Transaction::Type::eip1559; receipt1.status = EVMC_SUCCESS; - receipt1.gas_used = 0x2cd9b; + receipt1.cumulative_gas_used = 0x2cd9b; receipt1.logs_bloom_filter = compute_bloom_filter(receipt1.logs); EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1}), 0x7199a3a86010634dc205a1cdd6ec609f70b954167583cb3acb6a2e3057916016_bytes32); } + +TEST(state_mpt_hash, pre_byzantium_receipt) +{ + // Block taken from Ethereum mainnet + // https://etherscan.io/txs?block=4276370 + + using namespace evmone::state; + + TransactionReceipt receipt0{ + .type = Transaction::Type::legacy, + .cumulative_gas_used = 0x8323, + .logs = {}, + .logs_bloom_filter = compute_bloom_filter(receipt0.logs), + .post_state = 0x4a8f9db452b100f9ec85830785b2d1744c3e727561c334c4f18022daa113290a_bytes32, + }; + + TransactionReceipt receipt1{ + .type = Transaction::Type::legacy, + .cumulative_gas_used = 0x10646, + .logs = {}, + .logs_bloom_filter = compute_bloom_filter(receipt1.logs), + .post_state = 0xb14ab7c32b3e126591731850976a15e2359c1f3628f1b0ff37776c210b9cadb8_bytes32, + }; + + TransactionReceipt receipt2{ + .type = Transaction::Type::legacy, + .cumulative_gas_used = 0x1584e, + .logs = {}, + .logs_bloom_filter = compute_bloom_filter(receipt2.logs), + .post_state = 0x7bda915deb201cae321d31028d322877e8fb98264db3ffcbfec7ea7b9b2106b1_bytes32, + }; + + EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1, receipt2}), + 0x8a4fa43a95939b06ad13ce8cd08e026ae6e79ea3c5fc80c732d252e2769ce778_bytes32); +} + +TEST(state_mpt_hash, mpt_hashing_test) +{ + // The same data as in 'statetest_withdrawals.withdrawals_warmup_test_case' unit test. + MPT trie; + + trie.insert("80"_hex, "db808094c000000000000000000000000000000000000001830186a0"_hex); + EXPECT_EQ( + trie.hash(), 0x4ae14e53d6bf2a9c73891aef9d2e6373ff06d400b6e7727b17a5eceb5e8dec9d_bytes32); + + trie.insert("01"_hex, "db018094c000000000000000000000000000000000000002830186a0"_hex); + EXPECT_EQ( + trie.hash(), 0xc5128234c0282b256e2aa91ddc783ddb2c21556766f2e11e64159561b59f8ac7_bytes32); + + trie.insert("0x02"_hex, "0xdb028094c000000000000000000000000000000000000003830186a0"_hex); + trie.insert("0x03"_hex, "0xdb038094c000000000000000000000000000000000000004830186a0"_hex); + trie.insert("0x04"_hex, "0xdb048094c000000000000000000000000000000000000005830186a0"_hex); + trie.insert("0x05"_hex, "0xdb058094c000000000000000000000000000000000000006830186a0"_hex); + trie.insert("0x06"_hex, "0xdb068094c000000000000000000000000000000000000007830186a0"_hex); + trie.insert("0x07"_hex, "0xdb078094c000000000000000000000000000000000000008830186a0"_hex); + trie.insert("0x08"_hex, "0xdb088094c000000000000000000000000000000000000009830186a0"_hex); + trie.insert("0x09"_hex, "0xdb098094c000000000000000000000000000000000000010830186a0"_hex); + + EXPECT_EQ( + trie.hash(), 0xaa45c53e9f7d6a8362f80876029915da00b1441ef39eb9bbb74f98465ff433ad_bytes32); +} diff --git a/test/unittests/state_mpt_test.cpp b/test/unittests/state_mpt_test.cpp index 1f757acb..808d446d 100644 --- a/test/unittests/state_mpt_test.cpp +++ b/test/unittests/state_mpt_test.cpp @@ -4,17 +4,21 @@ #include #include +#include #include #include #include +#include +#include +using namespace intx::literals; using namespace evmone; using namespace evmone::state; -using namespace intx; +using namespace evmone::test; TEST(state_mpt, empty_trie) { - EXPECT_EQ(MPT{}.hash(), emptyMPTHash); + EXPECT_EQ(MPT{}.hash(), EMPTY_MPT_HASH); } TEST(state_mpt, single_account_v1) @@ -26,7 +30,7 @@ TEST(state_mpt, single_account_v1) constexpr auto addr = 0x0000000000000000000000000000000000000002_address; constexpr uint64_t nonce = 0; constexpr auto balance = 1_u256; - constexpr auto storage_hash = emptyMPTHash; + constexpr auto storage_hash = EMPTY_MPT_HASH; constexpr auto code_hash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32; @@ -85,10 +89,33 @@ TEST(state_mpt, branch_node_example1) EXPECT_EQ(hex(trie.hash()), "1aaa6f712413b9a115730852323deb5f5d796c29151a60a1f55f41a25354cd26"); } +TEST(state_mpt, branch_node_of_3) +{ + // A trie of single branch node and three leaf nodes with paths of length 2. + // The branch node has leaf nodes at positions [0], [1] and [2]. All leaves have path 0. + // {0:0 1:0 2:0} + + MPT trie; + trie.insert("00"_hex, "X"_b); + trie.insert("10"_hex, "Y"_b); + trie.insert("20"_hex, "Z"_b); + EXPECT_EQ(hex(trie.hash()), "5c5154e8d108dcf8b9946c8d33730ec8178345ce9d36e6feed44f0134515482d"); +} + +TEST(state_mpt, leaf_node_with_empty_path) +{ + // Both inserted leaves have empty path in the end. + // 0:{0:"X", 1:"Y"} + MPT trie; + trie.insert("00"_hex, "X"_b); + trie.insert("01"_hex, "Y"_b); + EXPECT_EQ(hex(trie.hash()), "0a923005d10fbd4e571655cec425db7c5091db03c33891224073a55d3abc2415"); +} + TEST(state_mpt, extension_node_example1) { // A trie of an extension node followed by a branch node with - // two leafs with single nibble paths. + // two leaves with single nibble paths. // 5858:{4:1, 5:a} auto value1 = "v___________________________1"_b; @@ -118,7 +145,7 @@ TEST(state_mpt, extension_node_example1) TEST(state_mpt, extension_node_example2) { // A trie of an extension node followed by a branch node with - // two leafs with longer paths. + // two leaves with longer paths. // 585:{8:41, 9:5a} auto value1 = "v___________________________1"_b; @@ -155,6 +182,69 @@ TEST(state_mpt, extension_node_example2) EXPECT_EQ(hex(trie.hash()), "ac28c08fa3ff1d0d2cc9a6423abb7af3f4dcc37aa2210727e7d3009a9b4a34e8"); } +TEST(state_mpt, keys_length_desc) +{ + const auto k127 = rlp::encode(127); + const auto k128 = rlp::encode(128); + EXPECT_EQ(k127, "7f"_hex); + EXPECT_EQ(k128, "8180"_hex); + + MPT asc; + asc.insert(k127, {}); + asc.insert(k128, {}); + EXPECT_EQ(hex(asc.hash()), "2fb7f2dee94138d79248ea2545a3ba1ceecb39e2037ed4e1d571c4d8bfbfa535"); + + MPT desc; + desc.insert(k128, {}); + desc.insert(k127, {}); + EXPECT_EQ(hex(desc.hash()), "2fb7f2dee94138d79248ea2545a3ba1ceecb39e2037ed4e1d571c4d8bfbfa535"); +} + +// In Ethereum lists are merkalized by creating a trie with keys of RLP-encoded enumeration. +// Therefore, the keys are of different length but none of them is a prefix of another. +// These tests create a list of N elements with empty values. +// TODO: Check with go-ethereum implementation. +static constexpr uint64_t LONG_LIST_SIZE = 100'000; +static constexpr auto LONG_LIST_HASH = + 0x70760bc8a0ebcc93601519d778576ae67a81731112df8d8c1518437a52f13520_bytes32; + +TEST(state_mpt, long_list_asc) +{ + uint64_t keys[LONG_LIST_SIZE]; + std::iota(std::begin(keys), std::end(keys), uint64_t{0}); + + MPT trie; + for (const auto key : keys) + trie.insert(rlp::encode(key), {}); + EXPECT_EQ(trie.hash(), LONG_LIST_HASH); +} + +TEST(state_mpt, long_list_desc) +{ + uint64_t keys[LONG_LIST_SIZE]; + std::iota(std::begin(keys), std::end(keys), uint64_t{0}); + std::ranges::reverse(keys); + + MPT trie; + for (const auto key : keys) + trie.insert(rlp::encode(key), {}); + EXPECT_EQ(trie.hash(), LONG_LIST_HASH); +} + +TEST(state_mpt, long_list_random) +{ + std::random_device rd; + std::mt19937_64 gen{rd()}; + uint64_t keys[LONG_LIST_SIZE]; + std::iota(std::begin(keys), std::end(keys), uint64_t{0}); + std::ranges::shuffle(keys, gen); + + MPT trie; + for (const auto key : keys) + trie.insert(rlp::encode(key), {}); + EXPECT_EQ(trie.hash(), LONG_LIST_HASH); +} + TEST(state_mpt, trie_topologies) { struct KVH @@ -164,6 +254,9 @@ TEST(state_mpt, trie_topologies) const char* hash_hex; }; + // The test cases are cross-checked with go-ethereum implementation. + // https://github.com/ethereum/go-ethereum/blob/7dea9c10cdb42e8c9f71b8b324cbe9222ab105cf/trie/stacktrie_test.go#L35 + // clang-format off const std::vector tests[] = { { // {0:0, 7:0, f:0} @@ -303,6 +396,38 @@ TEST(state_mpt, trie_topologies) {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"}, {"13aa", "x___________________________3", "ff0dc70ce2e5db90ee42a4c2ad12139596b890e90eb4e16526ab38fa465b35cf"}, }, + { // branch node with short values + {"01", "a", "b48605025f5f4b129d40a420e721aa7d504487f015fce85b96e52126365ef7dc"}, + {"80", "b", "2dc6b680daf74db067cb7aeaad73265ded93d96fce190fcbf64f498d475672ab"}, + {"ee", "c", "017dc705a54ac5328dd263fa1bae68d655310fb3e3f7b7bc57e9a43ddf99c4bf"}, + {"ff", "d", "bd5a3584d271d459bd4eb95247b2fc88656b3671b60c1125ffe7bc0b689470d0"}, + }, + { // ext node with short branch node, then becoming long + {"a0", "a", "a83e028cb1e4365935661a9fd36a5c65c30b9ab416eaa877424146ca2a69d088"}, + {"a1", "b", "f586a4639b07b01798ca65e05c253b75d51135ebfbf6f8d6e87c0435089e65f0"}, + {"a2", "c", "63e297c295c008e09a8d531e18d57f270b6bc403e23179b915429db948cd62e3"}, + {"a3", "d", "94a7b721535578e9381f1f4e4b6ec29f8bdc5f0458a30320684c562f5d47b4b5"}, + {"a4", "e", "4b7e66d1c81965cdbe8fab8295ef56bc57fefdc5733d4782d2f8baf630f083c6"}, + {"a5", "f", "2997e7b502198ce1783b5277faacf52b25844fb55a99b63e88bdbbafac573106"}, + {"a6", "g", "bee629dd27a40772b2e1a67ec6db270d26acdf8d3b674dfae27866ad6ae1f48b"}, + }, + { // branch node with short values, then long ones + {"a001", "v1", "b9cc982d995392b51e6787f1915f0b88efd4ad8b30f138da0a3e2242f2323e35"}, + {"b002", "v2", "a7b474bc77ef5097096fa0ee6298fdae8928c0bc3724e7311cd0fa9ed1942fc7"}, + {"c003", "v___________________________3", "dceb5bb7c92b0e348df988a8d9fc36b101397e38ebd405df55ba6ee5f14a264a"}, + {"d004", "v___________________________4", "36e60ecb86b9626165e1c6543c42ecbe4d83bca58e8e1124746961511fce362a"}, + }, + { // ext node to branch node with short values, then long ones + {"8002", "v1", "3258fcb3e9e7d7234ecd3b8d4743999e4ab3a21592565e0a5ca64c141e8620d9"}, + {"8004", "v2", "b6cb95b7024a83c17624a3c9bed09b4b5e8ed426f49f54b8ad13c39028b1e75a"}, + {"8008", "v___________________________3", "c769d82963abe6f0900bf69754738eeb2f84559777cfa87a44f54e1aab417871"}, + {"800d", "v___________________________4", "1cad1fdaab1a6fa95d7b780fd680030e423eb76669971368ba04797a8d9cdfc9"}, + }, + { // ext node with a child of size 31 (Y) and branch node with a child of size 31 (X) + {"000001", "ZZZZZZZZZ", "cef154b87c03c563408520ff9b26923c360cbc3ddb590c079bedeeb25a8c9c77"}, + {"000002", "Y", "2130735e600f612f6e657a32bd7be64ddcaec6512c5694844b19de713922895d"}, + {"000003", "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", "962c0fffdeef7612a4f7bff1950d67e3e81c878e48b9ae45b3b374253b050bd8"}, + }, }; // clang-format on @@ -321,7 +446,7 @@ TEST(state_mpt, trie_topologies) // Check if all insert order permutations give the same final hash. std::vector order(test.size()); std::iota(order.begin(), order.end(), size_t{0}); - while (std::next_permutation(order.begin(), order.end())) + while (std::ranges::next_permutation(order).found) { MPT trie; for (size_t i = 0; i < test.size(); ++i) diff --git a/test/unittests/state_new_account_address_test.cpp b/test/unittests/state_new_account_address_test.cpp index c406072a..99bf07bb 100644 --- a/test/unittests/state_new_account_address_test.cpp +++ b/test/unittests/state_new_account_address_test.cpp @@ -7,53 +7,46 @@ using namespace evmc; using namespace evmc::literals; -inline constexpr auto addr = evmone::state::compute_new_account_address; inline constexpr uint64_t nonces[] = {0, 1, 0x80, 0xffffffffffffffff}; inline constexpr address senders[] = { 0x00_address, 0x01_address, 0x8000000000000000000000000000000000000000_address}; -inline const bytes init_codes[] = {bytes{}, bytes{0xFE}}; -inline constexpr bytes32 salts[] = { - 0x00_bytes32, 0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0_bytes32}; TEST(state_new_account_address, create) { - for (const auto& ic : init_codes) // Init-code doesn't affect CREATE. - { - auto s = senders[0]; - EXPECT_EQ(addr(s, nonces[0], {}, ic), 0xbd770416a3345f91e4b34576cb804a576fa48eb1_address); - EXPECT_EQ(addr(s, nonces[3], {}, ic), 0x1262d73ea59d3a661bf8751d16cf1a5377149e75_address); - - s = senders[1]; - EXPECT_EQ(addr(s, nonces[0], {}, ic), 0x522b3294e6d06aa25ad0f1b8891242e335d3b459_address); - EXPECT_EQ(addr(s, nonces[1], {}, ic), 0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1_address); - EXPECT_EQ(addr(s, nonces[2], {}, ic), 0x09c1ef8f55c61b94e8b92a55d0891d408a991e18_address); - EXPECT_EQ(addr(s, nonces[3], {}, ic), 0x001567239734aeadea21023c2a7c0d9bb9ae4af9_address); - - s = senders[2]; - EXPECT_EQ(addr(s, nonces[0], {}, ic), 0x3cb1045aee4a06f522ea2b69e4f3d21ed3c135d1_address); - EXPECT_EQ(addr(s, nonces[3], {}, ic), 0xe1aa03e4a7b6991d69aff8ece53ceafdf347082e_address); - - const auto beacon_deposit_address = - addr(0xb20a608c624Ca5003905aA834De7156C68b2E1d0_address, 0, {}, ic); - EXPECT_EQ(beacon_deposit_address, 0x00000000219ab540356cbb839cbe05303d7705fa_address); - } -} + constexpr auto addr = evmone::state::compute_create_address; -TEST(state_new_account_address, create2) -{ - for (const auto n : nonces) // Nonce doesn't affect CREATE2. - { - EXPECT_EQ(addr(senders[0], n, salts[0], init_codes[0]), - 0xe33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0_address); + auto s = senders[0]; + EXPECT_EQ(addr(s, nonces[0]), 0xbd770416a3345f91e4b34576cb804a576fa48eb1_address); + EXPECT_EQ(addr(s, nonces[3]), 0x1262d73ea59d3a661bf8751d16cf1a5377149e75_address); + + s = senders[1]; + EXPECT_EQ(addr(s, nonces[0]), 0x522b3294e6d06aa25ad0f1b8891242e335d3b459_address); + EXPECT_EQ(addr(s, nonces[1]), 0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1_address); + EXPECT_EQ(addr(s, nonces[2]), 0x09c1ef8f55c61b94e8b92a55d0891d408a991e18_address); + EXPECT_EQ(addr(s, nonces[3]), 0x001567239734aeadea21023c2a7c0d9bb9ae4af9_address); - EXPECT_EQ(addr(senders[2], n, salts[0], init_codes[1]), - 0x3517dea701ed18fc4a99dc111c5946e1f1541dad_address); + s = senders[2]; + EXPECT_EQ(addr(s, nonces[0]), 0x3cb1045aee4a06f522ea2b69e4f3d21ed3c135d1_address); + EXPECT_EQ(addr(s, nonces[3]), 0xe1aa03e4a7b6991d69aff8ece53ceafdf347082e_address); - EXPECT_EQ(addr(senders[1], n, salts[1], init_codes[0]), - 0x7be1c1cb3b8298f21c56add66defce03e2d32604_address); + const auto beacon_roots1 = addr(0xb20a608c624Ca5003905aA834De7156C68b2E1d0_address, 0); + EXPECT_EQ(beacon_roots1, 0x00000000219ab540356cbb839cbe05303d7705fa_address); - EXPECT_EQ(addr(senders[2], n, salts[1], init_codes[1]), - 0x8f459e65c8f00a9c0c0493de7b0c61c3c27f7384_address); - } + const auto beacon_roots2 = addr(0x0B799C86a49DEeb90402691F1041aa3AF2d3C875_address, 0); + EXPECT_EQ(beacon_roots2, 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address); +} + +TEST(state_new_account_address, create2) +{ + constexpr auto addr = evmone::state::compute_create2_address; + constexpr auto z0 = 0x00_bytes32; + constexpr auto z1 = 0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0_bytes32; + const auto i0 = bytes{}; + const auto i1 = bytes{0xFE}; + + EXPECT_EQ(addr(senders[0], z0, i0), 0xe33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0_address); + EXPECT_EQ(addr(senders[2], z0, i1), 0x3517dea701ed18fc4a99dc111c5946e1f1541dad_address); + EXPECT_EQ(addr(senders[1], z1, i0), 0x7be1c1cb3b8298f21c56add66defce03e2d32604_address); + EXPECT_EQ(addr(senders[2], z1, i1), 0x8f459e65c8f00a9c0c0493de7b0c61c3c27f7384_address); } diff --git a/test/unittests/state_precompiles_test.cpp b/test/unittests/state_precompiles_test.cpp new file mode 100644 index 00000000..59066090 --- /dev/null +++ b/test/unittests/state_precompiles_test.cpp @@ -0,0 +1,54 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +using namespace evmc; +using namespace evmone::state; + +TEST(state_precompiles, is_precompile) +{ + for (int r = 0; r <= EVMC_MAX_REVISION; ++r) + { + const auto rev = static_cast(r); + + EXPECT_FALSE(is_precompile(rev, 0x00_address)); + + // Frontier: + EXPECT_TRUE(is_precompile(rev, 0x01_address)); + EXPECT_TRUE(is_precompile(rev, 0x02_address)); + EXPECT_TRUE(is_precompile(rev, 0x03_address)); + EXPECT_TRUE(is_precompile(rev, 0x04_address)); + + // Byzantium: + EXPECT_EQ(is_precompile(rev, 0x05_address), rev >= EVMC_BYZANTIUM); + EXPECT_EQ(is_precompile(rev, 0x06_address), rev >= EVMC_BYZANTIUM); + EXPECT_EQ(is_precompile(rev, 0x07_address), rev >= EVMC_BYZANTIUM); + EXPECT_EQ(is_precompile(rev, 0x08_address), rev >= EVMC_BYZANTIUM); + + // Istanbul: + EXPECT_EQ(is_precompile(rev, 0x09_address), rev >= EVMC_ISTANBUL); + + // Cancun: + EXPECT_EQ(is_precompile(rev, 0x0a_address), rev >= EVMC_CANCUN); + + // Prague: + EXPECT_EQ(is_precompile(rev, 0x0b_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x0c_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x0d_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x0e_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x0f_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x10_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x11_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x12_address), rev >= EVMC_PRAGUE); + EXPECT_EQ(is_precompile(rev, 0x13_address), rev >= EVMC_PRAGUE); + + // Future? + EXPECT_FALSE(is_precompile(rev, 0x14_address)); + EXPECT_FALSE(is_precompile(rev, 0x15_address)); + EXPECT_FALSE(is_precompile(rev, 0x16_address)); + EXPECT_FALSE(is_precompile(rev, 0x17_address)); + } +} diff --git a/test/unittests/state_rlp_test.cpp b/test/unittests/state_rlp_test.cpp index 781665fa..4488ca2e 100644 --- a/test/unittests/state_rlp_test.cpp +++ b/test/unittests/state_rlp_test.cpp @@ -4,32 +4,37 @@ #include #include +#include #include #include #include #include -using namespace evmone; using namespace evmc::literals; using namespace intx; +using namespace evmone; +using namespace evmone::test; using namespace testing; static constexpr auto emptyBytesHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32; -static constexpr auto emptyMPTHash = - 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32; - TEST(state_rlp, empty_bytes_hash) { EXPECT_EQ(keccak256({}), emptyBytesHash); } +TEST(state_rlp, empty_list_hash) +{ + EXPECT_EQ(keccak256(bytes{0xc0}), EmptyListHash); // Hash of empty RLP list: 0xc0. + EXPECT_EQ(keccak256(rlp::encode(std::vector{})), EmptyListHash); +} + TEST(state_rlp, empty_mpt_hash) { const auto rlp_null = rlp::encode(0); EXPECT_EQ(rlp_null, bytes{0x80}); - EXPECT_EQ(keccak256(rlp_null), emptyMPTHash); + EXPECT_EQ(keccak256(rlp_null), state::EMPTY_MPT_HASH); } TEST(state_rlp, encode_string_short) @@ -85,7 +90,7 @@ TEST(state_rlp, encode_account_with_balance) "a0 56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" "a0 c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"_hex; - const auto r = rlp::encode_tuple(uint64_t{0}, 1_u256, emptyMPTHash, emptyBytesHash); + const auto r = rlp::encode_tuple(uint64_t{0}, 1_u256, state::EMPTY_MPT_HASH, emptyBytesHash); EXPECT_EQ(r, expected); } @@ -204,7 +209,7 @@ TEST(state_rlp, tx_to_rlp_legacy) // https://eips.ethereum.org/EIPS/eip-155 state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::legacy; + tx.type = evmone::state::Transaction::Type::legacy; tx.data = ""_b; tx.gas_limit = 21000; tx.max_gas_price = 20000000000; @@ -239,7 +244,7 @@ TEST(state_rlp, tx_to_rlp_legacy_with_data) // https://etherscan.io/tx/0x033e9f8db737193d4666911a164e218d58d80edc64f4ed393d0c48c1ce2673e7 state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::legacy; + tx.type = evmone::state::Transaction::Type::legacy; tx.data = "0xa0712d680000000000000000000000000000000000000000000000000000000000000003"_hex; tx.gas_limit = 421566; tx.max_gas_price = 14829580649; @@ -278,7 +283,7 @@ TEST(state_rlp, tx_to_rlp_eip1559) state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip1559; + tx.type = evmone::state::Transaction::Type::eip1559; tx.data = ""_b; tx.gas_limit = 30000; tx.max_gas_price = 14237787676; @@ -320,7 +325,7 @@ TEST(state_rlp, tx_to_rlp_eip1559_with_data) // https://etherscan.io/tx/0xf9400dd4722908fa7b8d514429aebfd4cd04aaa9faaf044554d2f550422baef9 state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip1559; + tx.type = evmone::state::Transaction::Type::eip1559; tx.data = "095ea7b3" "0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582" @@ -361,51 +366,77 @@ TEST(state_rlp, tx_to_rlp_eip1559_with_data) TEST(state_rlp, tx_to_rlp_eip1559_invalid_v_value) { state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip1559; - tx.data = ""_hex; - tx.gas_limit = 1; - tx.max_gas_price = 1; - tx.max_priority_gas_price = 1; - tx.sender = 0x0000000000000000000000000000000000000000_address; - tx.to = 0x0000000000000000000000000000000000000000_address; - tx.value = 0; - tx.access_list = {}; - tx.nonce = 47; - tx.r = 0x0000000000000000000000000000000000000000000000000000000000000000_u256; - tx.s = 0x0000000000000000000000000000000000000000000000000000000000000000_u256; - tx.v = 2; + tx.type = evmone::state::Transaction::Type::eip1559; tx.chain_id = 1; + tx.nonce = 2; + tx.max_priority_gas_price = 3; + tx.max_gas_price = 4; + tx.gas_limit = 5; + tx.to = {}; + tx.value = 6; + tx.data = "da"_hex; + tx.access_list = {}; + tx.v = 7; + tx.r = 8_u256; + tx.s = 9_u256; - EXPECT_THAT([tx]() { rlp::encode(tx); }, - ThrowsMessage("`v` value for eip1559 transaction must be 0 or 1")); + const auto rlp_rep = rlp::encode(tx); + EXPECT_EQ(hex(rlp_rep), + "02" // tx_type + "cd" // rlp list start + "01" // chain_id + "02" // nonce + "03" // max_priority_fee_per_gas + "04" // max_fee_per_gas + "05" // gas_limit + "80" // to + "06" // value + "81da" // data + "c0" // access_list + "07" // sig_parity + "08" // sig_r + "09" // sig_s + ); } TEST(state_rlp, tx_to_rlp_eip2930_invalid_v_value) { state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip2930; - tx.data = ""_hex; - tx.gas_limit = 1; - tx.max_gas_price = 1; - tx.max_priority_gas_price = 1; - tx.sender = 0x0000000000000000000000000000000000000000_address; - tx.to = 0x0000000000000000000000000000000000000000_address; - tx.value = 0; - tx.access_list = {}; - tx.nonce = 47; - tx.r = 0x0000000000000000000000000000000000000000000000000000000000000000_u256; - tx.s = 0x0000000000000000000000000000000000000000000000000000000000000000_u256; - tx.v = 2; + tx.type = evmone::state::Transaction::Type::access_list; tx.chain_id = 1; + tx.nonce = 2; + tx.max_gas_price = 3; + tx.gas_limit = 4; + tx.to = {}; + tx.value = 5; + tx.data = "da"_hex; + tx.access_list = {}; + tx.v = 6; + tx.r = 7_u256; + tx.s = 8_u256; - EXPECT_THAT([tx]() { rlp::encode(tx); }, - ThrowsMessage("`v` value for eip2930 transaction must be 0 or 1")); + const auto rlp_rep = rlp::encode(tx); + EXPECT_EQ(hex(rlp_rep), + "01" // tx_type + "cc" // rlp list start + "01" // chain_id + "02" // nonce + "03" // max_fee_per_gas + "04" // gas_limit + "80" // to + "05" // value + "81da" // data + "c0" // access_list + "06" // sig_parity + "07" // sig_r + "08" // sig_s + ); } TEST(state_rlp, tx_to_rlp_eip1559_with_non_empty_access_list) { state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip1559; + tx.type = evmone::state::Transaction::Type::eip1559; tx.data = "00"_hex; tx.gas_limit = 0x3d0900; tx.max_gas_price = 0x7d0; @@ -431,7 +462,7 @@ TEST(state_rlp, tx_to_rlp_eip2930_with_non_empty_access_list) // https://etherscan.io/tx/0xf076e75aa935552e20e5d9fd4d1dda4ff33399ff3d6ac22843ae646f82c385d4 state::Transaction tx{}; - tx.kind = evmone::state::Transaction::Kind::eip2930; + tx.type = evmone::state::Transaction::Type::access_list; tx.data = "0x095ea7b3000000000000000000000000f17d23136b4fead139f54fb766c8795faae09660ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex; tx.gas_limit = 51253; @@ -451,3 +482,127 @@ TEST(state_rlp, tx_to_rlp_eip2930_with_non_empty_access_list) EXPECT_EQ(keccak256(rlp::encode(tx)), 0xf076e75aa935552e20e5d9fd4d1dda4ff33399ff3d6ac22843ae646f82c385d4_bytes32); } + +TEST(state_rlp, tx_to_rlp_blob) +{ + state::Transaction tx{}; + + tx.type = evmone::state::Transaction::Type::blob; + tx.data = ""_b; + tx.gas_limit = 30000; + tx.max_gas_price = 14237787676; + tx.max_priority_gas_price = 0; + tx.sender = 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5_address; + tx.to = 0x535b918f3724001fd6fb52fcc6cbc220592990a3_address; + tx.value = 73360267083380739_u256; + tx.access_list = {}; + tx.nonce = 132949; + tx.r = 0x2fe690e16de3534bee626150596573d57cb56d0c2e48a02f64c0a03c1636ce2a_u256; + tx.s = 0x4814f3dc7dac2ee153a2456aa3968717af7400972167dfb00b1cce1c23b6dd9f_u256; + tx.v = 1; + tx.chain_id = 1; + tx.max_blob_gas_price = 4; + tx.blob_hashes = {0x0111111111111111111111111111111111111111111111111111111111111111_bytes32, + 0x0122222222222222222222222222222222222222222222222222222222222222_bytes32}; + + const auto rlp_rep = rlp::encode(tx); + EXPECT_EQ(rlp_rep, + "03" + "f8b7" + "01" + "83020755" + "80" + "850350a3661c" + "827530" + "94535b918f3724001fd6fb52fcc6cbc220592990a3" + "880104a0c63421f803" + "80" + "c0" + "04" + "f842" + "a00111111111111111111111111111111111111111111111111111111111111111" + "a00122222222222222222222222222222222222222222222222222222222222222" + "01" + "a02fe690e16de3534bee626150596573d57cb56d0c2e48a02f64c0a03c1636ce2a" + "a04814f3dc7dac2ee153a2456aa3968717af7400972167dfb00b1cce1c23b6dd9f"_hex); +} + +TEST(state_rlp, tx_to_rlp_blob_invalid_v_value) +{ + state::Transaction tx{}; + tx.type = evmone::state::Transaction::Type::blob; + tx.chain_id = 1; + tx.nonce = 2; + tx.max_priority_gas_price = 3; + tx.max_gas_price = 4; + tx.gas_limit = 5; + tx.to = 0x00_address; + tx.value = 6; + tx.data = "da"_hex; + tx.access_list = {}; + tx.max_blob_gas_price = 7; + tx.blob_hashes = {}; + tx.v = 8; + tx.r = 9_u256; + tx.s = 0xa_u256; + + const auto rlp_rep = rlp::encode(tx); + EXPECT_EQ(hex(rlp_rep), + "03" // tx_type + "e3" // rlp list start + "01" // chain_id + "02" // nonce + "03" // max_priority_fee_per_gas + "04" // max_fee_per_gas + "05" // gas_limit + "940000000000000000000000000000000000000000" // to + "06" // value + "81da" // data + "c0" // access_list + "07" // max_fee_per_blob_gas + "c0" // blob_versioned_hashes + "08" // sig_parity + "09" // sig_r + "0a" // sig_s + ); +} + +TEST(state_rlp, tx_to_rlp_blob_invalid_to_value) +{ + state::Transaction tx{}; + tx.type = evmone::state::Transaction::Type::blob; + tx.chain_id = 1; + tx.nonce = 2; + tx.max_priority_gas_price = 3; + tx.max_gas_price = 4; + tx.gas_limit = 5; + tx.to = {}; + tx.value = 6; + tx.data = "da"_hex; + tx.access_list = {}; + tx.max_blob_gas_price = 7; + tx.blob_hashes = {0x0b_bytes32}; + tx.v = 1; + tx.r = 9_u256; + tx.s = 0xa_u256; + + const auto rlp_rep = rlp::encode(tx); + EXPECT_EQ(hex(rlp_rep), + "03" // tx_type + "f0" // rlp list start + "01" // chain_id + "02" // nonce + "03" // max_priority_fee_per_gas + "04" // max_fee_per_gas + "05" // gas_limit + "80" // to + "06" // value + "81da" // data + "c0" // access_list + "07" // max_fee_per_blob_gas + "e1a0000000000000000000000000000000000000000000000000000000000000000b" // blob_versioned_hashes + "01" // sig_parity + "09" // sig_r + "0a" // sig_s + ); +} diff --git a/test/unittests/state_system_call_test.cpp b/test/unittests/state_system_call_test.cpp new file mode 100644 index 00000000..209234a0 --- /dev/null +++ b/test/unittests/state_system_call_test.cpp @@ -0,0 +1,66 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +using namespace evmc::literals; +using namespace evmone::state; +using namespace evmone::test; + +class state_system_call : public testing::Test +{ +protected: + evmc::VM vm{evmc_create_evmone()}; + State state; +}; + +TEST_F(state_system_call, non_existient) +{ + // Use MAX revision to invoke all activate system contracts. + system_call(state, {}, EVMC_MAX_REVISION, vm); + + EXPECT_EQ(state.get_accounts().size(), 0) << "State must remain unchanged"; +} + +TEST_F(state_system_call, beacon_roots) +{ + const BlockInfo block{.number = 1, .parent_beacon_block_root = 0xbeac04004a54_bytes32}; + state.insert( + BEACON_ROOTS_ADDRESS, {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)}); + + system_call(state, block, EVMC_CANCUN, vm); + + ASSERT_EQ(state.get_accounts().size(), 1); + EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr); + EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).nonce, 0); + EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).balance, 0); + const auto& storage = state.get(BEACON_ROOTS_ADDRESS).storage; + ASSERT_EQ(storage.size(), 2); + EXPECT_EQ(storage.at(0x01_bytes32).current, block.parent_beacon_block_root); + EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS)); +} + +TEST_F(state_system_call, history_storage) +{ + static constexpr auto NUMBER = 123456789; + static constexpr auto PREV_BLOCKHASH = 0xbbbb_bytes32; + const BlockInfo block{.number = NUMBER, .known_block_hashes = {{NUMBER - 1, PREV_BLOCKHASH}}}; + state.insert(HISTORY_STORAGE_ADDRESS, + {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)}); + + system_call(state, block, EVMC_PRAGUE, vm); + + ASSERT_EQ(state.get_accounts().size(), 1); + EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr); + EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).nonce, 0); + EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).balance, 0); + const auto& storage = state.get(HISTORY_STORAGE_ADDRESS).storage; + ASSERT_EQ(storage.size(), 2); + EXPECT_EQ(storage.at(bytes32{NUMBER}).current, PREV_BLOCKHASH); + EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS)); +} diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index b00769a3..804daa6d 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -3,6 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 #include "state_transition.hpp" +#include +#include +#include +#include +#include namespace evmone::test { @@ -10,66 +15,229 @@ void state_transition::SetUp() { pre.insert(tx.sender, {.nonce = 1, .balance = tx.gas_limit * tx.max_gas_price + tx.value + 1}); - // Default expectations. - expect.post[Coinbase].exists = true; + // Default expectation (coinbase is added later for valid txs only). expect.post[tx.sender].exists = true; } +class TraceCapture +{ + std::streambuf* m_orig_rdbuf = nullptr; + std::ostringstream m_trace_stream; + +public: + TraceCapture() { m_orig_rdbuf = std::clog.rdbuf(m_trace_stream.rdbuf()); } + ~TraceCapture() { std::clog.rdbuf(m_orig_rdbuf); } + [[maybe_unused]] std::string get_capture() const { return m_trace_stream.str(); } +}; + void state_transition::TearDown() { - auto& state = pre; - const auto res = evmone::state::transition(state, block, tx, rev, vm); - ASSERT_TRUE(holds_alternative(res)) - << std::get(res).message(); - const auto& receipt = std::get(res); - evmone::state::finalize(state, rev, block.coinbase, 0, block.withdrawals); - - EXPECT_EQ(receipt.status, expect.status); - if (expect.gas_used.has_value()) + // Validation: + + if (rev < EVMC_LONDON) { - EXPECT_EQ(receipt.gas_used, *expect.gas_used); + ASSERT_EQ(block.base_fee, 0); + ASSERT_EQ(tx.type, state::Transaction::Type::legacy); + } + if (tx.type == state::Transaction::Type::legacy) + { + ASSERT_EQ(tx.max_gas_price, tx.max_priority_gas_price); + } + + validate_state(pre, rev); + + // Execution: + + const auto trace = !expect.trace.empty(); + auto& selected_vm = trace ? tracing_vm : vm; + + /// Optionally enable trace capturing in form of a RAII object. + std::optional trace_capture; + if (trace) + trace_capture.emplace(); + + auto intra_state = pre.to_intra_state(); + const auto res = state::transition(intra_state, block, tx, rev, selected_vm, block.gas_limit, + state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); + state::finalize( + intra_state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); + TestState post{intra_state}; + + if (const auto expected_error = make_error_code(expect.tx_error)) + { + ASSERT_TRUE(holds_alternative(res)) + << "tx expected to be invalid with error: " << expected_error.message(); + const auto tx_error = std::get(res); + EXPECT_EQ(tx_error, expected_error) + << tx_error.message() << " vs " << expected_error.message(); + + EXPECT_EQ(post, pre) << "failed transaction has modified the state"; + } + else + { + ASSERT_TRUE(holds_alternative(res)) + << std::get(res).message(); + const auto& receipt = std::get(res); + + EXPECT_EQ(receipt.status, expect.status); + if (expect.gas_used.has_value()) + { + EXPECT_EQ(receipt.gas_used, *expect.gas_used); + } + // Update default expectations - valid transaction means coinbase exists unless explicitly + // requested otherwise + if (expect.post.find(Coinbase) == expect.post.end()) + expect.post[Coinbase].exists = true; + } + + if (trace) + { + if (expect.trace.starts_with('\n')) // It's easier to define expected trace with \n. + expect.trace.remove_prefix(1); + EXPECT_EQ(trace_capture->get_capture(), expect.trace); } for (const auto& [addr, expected_acc] : expect.post) { - const auto acc = state.find(addr); - if (!expected_acc.exists) + const auto ait = post.find(addr); + if (ait == post.end()) + { + EXPECT_FALSE(expected_acc.exists) << addr << ": should not exist"; + continue; + } + EXPECT_TRUE(expected_acc.exists) << addr << ": should exist"; + + const auto& acc = ait->second; + if (expected_acc.nonce.has_value()) + { + EXPECT_EQ(acc.nonce, *expected_acc.nonce) << addr << ": wrong nonce"; + } + if (expected_acc.balance.has_value()) + { + EXPECT_EQ(acc.balance, *expected_acc.balance) + << addr << ": balance " << to_string(acc.balance) << " vs " + << to_string(*expected_acc.balance); + } + if (expected_acc.code.has_value()) + { + EXPECT_EQ(acc.code, *expected_acc.code) << addr << ": wrong code"; + } + for (const auto& [key, expected_value] : expected_acc.storage) + { + // Lookup storage values. Map non-existing ones to 0. + const auto sit = acc.storage.find(key); + const auto& value = sit != acc.storage.end() ? sit->second : bytes32{}; + EXPECT_EQ(value, expected_value) << addr << ": wrong storage " << key; + } + for (const auto& [key, value] : acc.storage) { - EXPECT_EQ(acc, nullptr) << "account " << addr << " should not exist"; + // Find unexpected storage keys. This will also report entries with value 0. + EXPECT_TRUE(expected_acc.storage.contains(key)) + << addr << ": unexpected storage " << key << "=" << value; } - else + } + + for (const auto& [addr, _] : post) + { + EXPECT_TRUE(expect.post.contains(addr)) << addr << ": should not exist"; + } + + if (expect.state_hash) + { + EXPECT_EQ(mpt_hash(post), *expect.state_hash); + } + + if (!export_file_path.empty()) + export_state_test(res, post); +} + +namespace +{ +/// Converts EVM revision to the fork name commonly used in tests. +std::string_view to_test_fork_name(evmc_revision rev) noexcept +{ + switch (rev) + { + case EVMC_TANGERINE_WHISTLE: + return "EIP150"; + case EVMC_SPURIOUS_DRAGON: + return "EIP158"; + default: + return evmc::to_string(rev); + } +} +} // namespace + +void state_transition::export_state_test( + const std::variant& res, const TestState& post) +{ + json::json j; + auto& jt = j[export_test_name]; + + auto& jenv = jt["env"]; + jenv["currentNumber"] = hex0x(block.number); + jenv["currentTimestamp"] = hex0x(block.timestamp); + jenv["currentGasLimit"] = hex0x(block.gas_limit); + jenv["currentCoinbase"] = hex0x(block.coinbase); + jenv["currentBaseFee"] = hex0x(block.base_fee); + jenv["currentRandom"] = hex0x(block.prev_randao); + + jt["pre"] = to_json(pre); + + auto& jtx = jt["transaction"]; + if (tx.to.has_value()) + jtx["to"] = hex0x(*tx.to); + jtx["sender"] = hex0x(tx.sender); + jtx["secretKey"] = hex0x(SenderSecretKey); + jtx["nonce"] = hex0x(tx.nonce); + if (rev < EVMC_LONDON) + { + assert(tx.max_gas_price == tx.max_priority_gas_price); + jtx["gasPrice"] = hex0x(tx.max_gas_price); + } + else + { + jtx["maxFeePerGas"] = hex0x(tx.max_gas_price); + jtx["maxPriorityFeePerGas"] = hex0x(tx.max_priority_gas_price); + } + + jtx["data"][0] = hex0x(tx.data); + jtx["gasLimit"][0] = hex0x(tx.gas_limit); + jtx["value"][0] = hex0x(tx.value); + + // Force `accessLists` output even if empty. + if (tx.type >= Transaction::Type::access_list) + jtx["accessLists"][0] = json::json::array(); + + if (!tx.access_list.empty()) + { + auto& ja = jtx["accessLists"][0]; + for (const auto& [addr, storage_keys] : tx.access_list) { - ASSERT_NE(acc, nullptr) << "account " << addr << " should exist"; - if (expected_acc.nonce.has_value()) - { - EXPECT_EQ(acc->nonce, *expected_acc.nonce) << "account " << addr; - } - if (expected_acc.balance.has_value()) - { - EXPECT_EQ(acc->balance, *expected_acc.balance) - << to_string(acc->balance) << " vs " << to_string(*expected_acc.balance) - << " account " << addr; - } - if (expected_acc.code.has_value()) - { - EXPECT_EQ(acc->code, *expected_acc.code) << "account " << addr; - } - for (const auto& [key, value] : expected_acc.storage) - { - EXPECT_EQ(acc->storage[key].current, value) << "account " << addr << " key " << key; - } - for (const auto& [key, value] : acc->storage) - { - // Find unexpected storage keys. This will also report entries with value 0. - EXPECT_TRUE(expected_acc.storage.contains(key)) - << "unexpected storage key " << key << "=" << value.current << " in " << addr; - } + json::json je; + je["address"] = hex0x(addr); + auto& jstorage_keys = je["storageKeys"] = json::json::array(); + for (const auto& k : storage_keys) + jstorage_keys.emplace_back(hex0x(k)); + ja.emplace_back(std::move(je)); } } - for (const auto& [addr, _] : state.get_accounts()) + auto& jpost = jt["post"][to_test_fork_name(rev)][0]; + jpost["indexes"] = {{"data", 0}, {"gas", 0}, {"value", 0}}; + jpost["hash"] = hex0x(mpt_hash(post)); + + if (holds_alternative(res)) + { + jpost["expectException"] = get_tests_invalid_tx_message( + static_cast(std::get(res).value())); + jpost["logs"] = hex0x(logs_hash(std::vector())); + } + else { - EXPECT_TRUE(expect.post.contains(addr)) << "unexpected account " << addr; + jpost["logs"] = hex0x(logs_hash(std::get(res).logs)); } + + std::ofstream{export_file_path} << std::setw(2) << j; } } // namespace evmone::test diff --git a/test/unittests/state_transition.hpp b/test/unittests/state_transition.hpp index 886b50d3..713460b3 100644 --- a/test/unittests/state_transition.hpp +++ b/test/unittests/state_transition.hpp @@ -3,11 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "exportable_fixture.hpp" #include -#include +#include #include - -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#include namespace evmone::test { @@ -18,19 +18,24 @@ using namespace evmone::state; /// /// It takes the "pre" state and produces "post" state by applying the defined "tx" transaction. /// Then expectations declared in "except" are checked in the "post" state. -class state_transition : public testing::Test +class state_transition : public ExportableFixture { protected: /// The default sender address of the test transaction. /// Private key: 0x2b1263d2b. static constexpr auto Sender = 0xe100713FC15400D1e94096a545879E7c6407001e_address; + /// The secret (private) key of the Sender address. + static constexpr auto SenderSecretKey = + 0x00000000000000000000000000000000000000000000000000000002b1263d2b_bytes32; + /// The default destination address of the test transaction. static constexpr auto To = 0xc0de_address; static constexpr auto Coinbase = 0xc014bace_address; static inline evmc::VM vm{evmc_create_evmone()}; + static inline evmc::VM tracing_vm{evmc_create_evmone(), {{"trace", "1"}}}; struct ExpectedAccount { @@ -43,15 +48,31 @@ class state_transition : public testing::Test struct Expectation { + /// The transaction is invalid because of the given error. + /// The rest of Expectation is ignored if the error is expected. + ErrorCode tx_error = SUCCESS; + + /// The expected EVM status code of the transaction execution. evmc_status_code status = EVMC_SUCCESS; + + /// The expected amount of gas used by the transaction. std::optional gas_used; + /// The expected post-execution state. std::unordered_map post; + + std::optional state_hash; + + /// The expected EVM execution trace. If not empty transaction execution will be performed + /// with tracing enabled and the output compared. + std::string_view trace; }; evmc_revision rev = EVMC_SHANGHAI; + uint64_t block_reward = 0; BlockInfo block{ + .number = 1, // Some EVMs don't like blocks with number 0. .gas_limit = 1'000'000, .coinbase = Coinbase, .base_fee = 999, @@ -59,16 +80,21 @@ class state_transition : public testing::Test Transaction tx{ .gas_limit = block.gas_limit, .max_gas_price = block.base_fee + 1, - .max_priority_gas_price = 1, + .max_priority_gas_price = block.base_fee + 1, .sender = Sender, + .nonce = 1, }; - State pre; + TestState pre; Expectation expect; void SetUp() override; /// The test runner. void TearDown() override; + + /// Exports the test in the JSON State Test format to ExportableFixture::export_out. + void export_state_test( + const std::variant& res, const TestState& post); }; } // namespace evmone::test diff --git a/test/unittests/state_transition_block_test.cpp b/test/unittests/state_transition_block_test.cpp index bee2810c..33d51948 100644 --- a/test/unittests/state_transition_block_test.cpp +++ b/test/unittests/state_transition_block_test.cpp @@ -2,6 +2,7 @@ // Copyright 2023 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include "../utils/bytecode.hpp" #include "state_transition.hpp" using namespace evmc::literals; @@ -11,7 +12,62 @@ TEST_F(state_transition, block_apply_withdrawal) { static constexpr auto withdrawal_address = 0x8888_address; - block.withdrawals = {{withdrawal_address, 3}}; + block.withdrawals = {{0, 0, withdrawal_address, 3}}; tx.to = To; expect.post[withdrawal_address].balance = intx::uint256{3} * 1'000'000'000; } + +TEST_F(state_transition, known_block_hash) +{ + block.known_block_hashes = { + {1, 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32}, + {2, 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32}}; + block.number = 5; + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = sstore(0, blockhash(1)) + sstore(1, blockhash(2))}); + expect.post[To].storage[0x00_bytes32] = + 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32; + expect.post[To].storage[0x01_bytes32] = + 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32; +} + +TEST_F(state_transition, known_block_hash_fake) +{ + block.number = 2; + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = sstore(0, blockhash(0)) + sstore(1, blockhash(1))}); + expect.post[To].storage[0x00_bytes32] = + 0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d_bytes32; + expect.post[To].storage[0x01_bytes32] = + 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6_bytes32; +} + +TEST_F(state_transition, block_apply_ommers_reward) +{ + rev = EVMC_LONDON; + + static constexpr auto o1 = Ommer{0x0eeee1_address, 1}; + static constexpr auto o2 = Ommer{0x0eeee2_address, 3}; + + // Use high value 5 ETH to catch potential uint64 overflows. + block_reward = 5'000'000'000'000'000'000; + block.ommers = {o1, o2}; + tx.to = To; + expect.post[o1.beneficiary].balance = intx::uint256{block_reward} * (8 - o1.delta) / 8; + expect.post[o2.beneficiary].balance = intx::uint256{block_reward} * (8 - o2.delta) / 8; + + // Two ommers +1/32 * block_reward for each. +21000 cost of the tx goes to coinbase. + expect.post[Coinbase].balance = 21000 + intx::uint256{block_reward} + block_reward / 16; +} + +TEST_F(state_transition, eip7516_blob_base_fee) +{ + rev = EVMC_CANCUN; + + block.blob_base_fee = 0xabcd; + tx.to = To; + pre.insert(*tx.to, {.code = sstore(0x4a, OP_BLOBBASEFEE)}); + + expect.post[To].storage[0x4a_bytes32] = 0xabcd_bytes32; +} diff --git a/test/unittests/state_transition_call_test.cpp b/test/unittests/state_transition_call_test.cpp new file mode 100644 index 00000000..551310d4 --- /dev/null +++ b/test/unittests/state_transition_call_test.cpp @@ -0,0 +1,52 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, call_value_to_empty) +{ + rev = EVMC_LONDON; + static constexpr auto BENEFICIARY = 0xbe_address; + tx.to = To; + pre[To] = {.balance = 1, .code = call(BENEFICIARY).value(1)}; + pre[BENEFICIARY] = {}; + + expect.post[To].balance = 0; + expect.post[BENEFICIARY].balance = 1; +} + +TEST_F(state_transition, delegatecall_static_legacy) +{ + rev = EVMC_PRAGUE; + // Checks if DELEGATECALL forwards the "static" flag. + static constexpr auto CALLEE1 = 0xca11ee01_address; + static constexpr auto CALLEE2 = 0xca11ee02_address; + pre[CALLEE2] = { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = sstore(1, 0xcc_bytes32), + }; + pre[CALLEE1] = { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = ret(delegatecall(CALLEE2).gas(100'000)), + }; + tx.to = To; + pre[To] = { + .storage = {{0x01_bytes32, 0xdd_bytes32}, {0x02_bytes32, 0xdd_bytes32}}, + .code = sstore(1, staticcall(CALLEE1).gas(200'000)) + + sstore(2, returndatacopy(0, 0, returndatasize()) + mload(0)), + }; + + expect.gas_used = 131480; + // Outer call - success. + expect.post[To].storage[0x01_bytes32] = 0x01_bytes32; + // Inner call - no success. + expect.post[To].storage[0x02_bytes32] = 0x00_bytes32; + // SSTORE failed. + expect.post[CALLEE1].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[CALLEE2].storage[0x01_bytes32] = 0xdd_bytes32; +} diff --git a/test/unittests/state_transition_create_test.cpp b/test/unittests/state_transition_create_test.cpp index 60111972..17b8c6f3 100644 --- a/test/unittests/state_transition_create_test.cpp +++ b/test/unittests/state_transition_create_test.cpp @@ -10,8 +10,6 @@ using namespace evmone::test; TEST_F(state_transition, create2_factory) { - static constexpr auto create_address = 0xfd8e7707356349027a32d71eabc7cb0cf9d7cbb4_address; - const auto factory_code = calldatacopy(0, 0, calldatasize()) + create2().input(0, calldatasize()); const auto initcode = mstore8(0, push(0xFE)) + ret(0, 1); @@ -20,15 +18,334 @@ TEST_F(state_transition, create2_factory) tx.data = initcode; pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + const auto create_address = compute_create2_address(*tx.to, {}, initcode); expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // CREATE caller's nonce must be bumped expect.post[create_address].code = bytes{0xFE}; } -TEST_F(state_transition, create_tx) +TEST_F(state_transition, create_tx_empty) { - static constexpr auto create_address = 0x3442a1dec1e72f337007125aa67221498cdd759d_address; + // The default transaction without "to" address is a create transaction. + expect.post[compute_create_address(Sender, pre.get(Sender).nonce)] = { + .nonce = 1, .code = bytes{}}; + + // Example of checking the expected the post state MPT root hash. + expect.state_hash = 0x8ae438f7a4a14dbc25410dfaa12e95e7b36f311ab904b4358c3b544e06df4c50_bytes32; +} + +TEST_F(state_transition, create_tx) +{ tx.data = mstore8(0, push(0xFE)) + ret(0, 1); + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); expect.post[create_address].code = bytes{0xFE}; } + +TEST_F(state_transition, create_tx_failure) +{ + static constexpr auto create_address = 0x3442a1dec1e72f337007125aa67221498cdd759d_address; + + tx.data = bytecode{} + OP_INVALID; + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[create_address].exists = false; +} + +TEST_F(state_transition, create2_max_nonce) +{ + tx.to = To; + pre.insert(*tx.to, {.nonce = ~uint64_t{0}, .code = create2()}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; // Nonce is unchanged. +} + +TEST_F(state_transition, code_deployment_out_of_gas_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled + block.base_fee = 0; + const auto initcode = ret(0, 5000); // create contract with a lot of zeros, deploy cost 1M + + tx.to = To; + tx.gas_limit = 1000000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + expect.post[To].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, code_deployment_out_of_gas_f) +{ + rev = EVMC_FRONTIER; + block.base_fee = 0; + const auto initcode = ret(0, 1000); // create contract with a lot of zeros + + tx.to = To; + tx.gas_limit = 100000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + const auto created = compute_create_address(To, pre.get(To).nonce); + expect.post[created].code = bytes{}; // code deployment failure creates empty account + expect.post[created].nonce = 0; + expect.post[To].storage[0x00_bytes32] = to_bytes32(created); // address of created empty +} + +TEST_F(state_transition, code_deployment_out_of_gas_storage_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled + block.base_fee = 0; + const auto initcode = sstore(0, 1) // set storage + + ret(0, 5000); // create contract with a lot of zeros + + tx.to = To; + tx.gas_limit = 1000000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + expect.post[To].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, code_deployment_out_of_gas_storage_f) +{ + rev = EVMC_FRONTIER; + block.base_fee = 0; + const auto initcode = sstore(0, 1) // set storage + + ret(0, 1000); // create contract with a lot of zeros + + tx.to = To; + tx.gas_limit = 100000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + expect.post[To].exists = true; + const auto created = compute_create_address(To, pre.get(To).nonce); + expect.post[created].code = bytes{}; // code deployment failure creates empty account + expect.post[created].nonce = 0; + expect.post[created].storage[0x00_bytes32] = 0x01_bytes32; // storage stays + expect.post[To].storage[0x00_bytes32] = to_bytes32(created); + expect.gas_used = 93134; +} + +TEST_F(state_transition, code_deployment_out_of_gas_refund_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled + block.base_fee = 0; + const auto initcode = sstore(0, 1) // set storage + + sstore(0, 0) // gas refund + + ret(0, 5000); // create contract with a lot of zeros + + tx.to = To; + tx.gas_limit = 1000000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + expect.post[To].storage[0x00_bytes32] = 0x00_bytes32; + expect.gas_used = 990207; +} + +TEST_F(state_transition, code_deployment_out_of_gas_refund_f) +{ + rev = EVMC_FRONTIER; + block.base_fee = 0; + const auto initcode = sstore(0, 1) // set storage + + sstore(0, 0) // gas refund + + ret(0, 1000); // create contract with a lot of zeros + + tx.to = To; + tx.gas_limit = 100000; + pre.insert(To, {.code = mstore(0, push(initcode)) + + sstore(0, create().input(32 - initcode.size(), initcode.size()))}); + + expect.post[To].exists = true; + const auto created = compute_create_address(To, pre.get(To).nonce); + expect.post[created].code = bytes{}; // code deployment failure creates empty account + expect.post[created].nonce = 0; + expect.post[created].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[To].storage[0x00_bytes32] = to_bytes32(created); + expect.gas_used = 83140; +} + +TEST_F(state_transition, create_tx_collision) +{ + static constexpr auto CREATED = 0x3442a1dec1e72f337007125aa67221498cdd759d_address; + + pre.insert(CREATED, {.nonce = 2}); + + expect.status = EVMC_FAILURE; + expect.post[CREATED].nonce = 2; +} + +TEST_F(state_transition, create_tx_collision_storage) +{ + // Test create address collision with an account only having storage (EIP-7610). + const auto created = compute_create_address(Sender, pre[Sender].nonce); + pre[created] = {.storage = {{0x00_bytes32, 0x01_bytes32}}}; + + expect.status = EVMC_FAILURE; + expect.post[created].storage[0x00_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create_collision) +{ + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create()}); + pre.insert(CREATED, {.nonce = 2}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[CREATED].nonce = pre.get(CREATED).nonce; +} + +TEST_F(state_transition, create_collision_storage) +{ + // Test create address collision with an account only having storage (EIP-7610). + tx.to = To; + pre[To] = {.code = sstore(0, create())}; + const auto created = compute_create_address(To, pre[To].nonce); + pre[created] = {.storage = {{0x00_bytes32, 0x01_bytes32}}}; + + expect.post[To].nonce = pre[To].nonce + 1; + expect.post[To].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[created].storage[0x00_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create_collision_revert) +{ + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + pre.insert(CREATED, {.nonce = 2}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].nonce = pre.get(CREATED).nonce; +} + +TEST_F(state_transition, create_prefunded_revert) +{ + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + pre.insert(CREATED, {.balance = 2}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].nonce = pre.get(CREATED).nonce; +} + +TEST_F(state_transition, create_revert) +{ + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = false; +} + +TEST_F(state_transition, create_revert_sd) +{ + rev = EVMC_SPURIOUS_DRAGON; + block.base_fee = 0; + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = false; +} + +TEST_F(state_transition, create_revert_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; + block.base_fee = 0; + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = false; +} + +TEST_F(state_transition, create_collision_empty_revert) +{ + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + pre.insert(CREATED, {}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = true; +} + +TEST_F(state_transition, create_collision_empty_revert_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; + block.base_fee = 0; + static constexpr auto CREATED = 0x8bbc3514477d75ec797bbe4e19d7961660bb849c_address; + + tx.to = To; + pre.insert(*tx.to, {.code = create() + OP_INVALID}); + pre.insert(CREATED, {}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = true; +} + +TEST_F(state_transition, touch_create_collision_empty_revert) +{ + static constexpr auto CREATED = 0x11f72042f0f1c9d8a1aeffc3680d0b41dd7769a7_address; + static constexpr auto REVERT_PROXY = 0x94_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(CREATED) + call(REVERT_PROXY).gas(0xffff)}); + pre.insert(REVERT_PROXY, {.code = create() + OP_INVALID}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = false; + expect.post[REVERT_PROXY].exists = true; +} + +TEST_F(state_transition, touch_create_collision_empty_revert_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; + block.base_fee = 0; + static constexpr auto CREATED = 0x11f72042f0f1c9d8a1aeffc3680d0b41dd7769a7_address; + static constexpr auto REVERT_PROXY = 0x94_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(CREATED) + call(REVERT_PROXY).gas(0xffff)}); + pre.insert(REVERT_PROXY, {.code = create() + OP_INVALID}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[CREATED].exists = true; + expect.post[REVERT_PROXY].exists = true; +} + +TEST_F(state_transition, created_code_hash) +{ + const auto runtime_code = bytes{0xc0}; + ASSERT_EQ(runtime_code.size(), 1); + const auto initcode = mstore8(0, push(runtime_code)) + ret(0, runtime_code.size()); + tx.to = To; + pre.insert(To, + {.code = mstore(0, push(initcode)) + create().input(32 - initcode.size(), initcode.size()) + + sstore(0, bytecode{OP_EXTCODEHASH})}); + + const auto created = compute_create_address(To, pre.get(To).nonce); + expect.post[created].code = runtime_code; + expect.post[To].storage[0x00_bytes32] = keccak256(runtime_code); +} diff --git a/test/unittests/state_transition_eip663_test.cpp b/test/unittests/state_transition_eip663_test.cpp new file mode 100644 index 00000000..9c9ba593 --- /dev/null +++ b/test/unittests/state_transition_eip663_test.cpp @@ -0,0 +1,49 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, dupn) +{ + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, + { + .code = eof_bytecode( + push(1) + 255 * push(2) + OP_DUPN + "ff" + sstore(0) + sstore(1) + OP_STOP, 258), + }); + expect.post[*tx.to].storage[0x00_bytes32] = 0x01_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; +} + +TEST_F(state_transition, swapn) +{ + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, + { + .code = eof_bytecode( + push(1) + 256 * push(2) + OP_SWAPN + "ff" + sstore(0) + sstore(1) + OP_STOP, 258), + }); + expect.post[*tx.to].storage[0x00_bytes32] = 0x01_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; +} + +TEST_F(state_transition, exchange) +{ + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(push(1) + push(2) + push(3) + OP_EXCHANGE + "00" + + sstore(0) + sstore(1) + sstore(2) + OP_STOP, + 4), + }); + expect.post[*tx.to].storage[0x00_bytes32] = 0x03_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[*tx.to].storage[0x02_bytes32] = 0x02_bytes32; +} diff --git a/test/unittests/state_transition_eof_calls_test.cpp b/test/unittests/state_transition_eof_calls_test.cpp new file mode 100644 index 00000000..c1000d4c --- /dev/null +++ b/test/unittests/state_transition_eof_calls_test.cpp @@ -0,0 +1,945 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, eof1_extdelegatecall_eof1) +{ + rev = EVMC_PRAGUE; + + constexpr auto callee = 0xca11ee_address; + pre.insert(callee, + { + .storage = {{0x02_bytes32, 0xdd_bytes32}}, + .code = + eof_bytecode(sstore(2, 0xcc_bytes32) + mstore(0, 0x010203_bytes32) + ret(0, 32), 2), + }); + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x02_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndataload(0)) + OP_STOP, 3), + }); + + expect.gas_used = 50742; + expect.post[*tx.to].storage[0x01_bytes32] = 0x010203_bytes32; + // SSTORE executed on caller. + expect.post[callee].storage[0x02_bytes32] = 0xdd_bytes32; + expect.post[*tx.to].storage[0x02_bytes32] = 0xcc_bytes32; +} + +TEST_F(state_transition, eof1_extdelegatecall_legacy) +{ + rev = EVMC_PRAGUE; + + constexpr auto callee = 0xca11ee_address; + pre.insert(callee, { + .code = sstore(3, 0xcc_bytes32), + }); + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}, {0x02_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + sstore(1, extdelegatecall(callee)) + sstore(2, returndatasize()) + OP_STOP, 3), + }); + + expect.gas_used = 28817; // Low gas usage because EXTDELEGATECALL fails lightly + // Call - light failure. + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + // Returndata empty. + expect.post[*tx.to].storage[0x02_bytes32] = 0x00_bytes32; + // SSTORE not executed. + expect.post[callee].storage[0x03_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x03_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, extdelegatecall_static) +{ + rev = EVMC_PRAGUE; + // Checks if EXTDELEGATECALL forwards the "static" flag. + constexpr auto callee1 = 0xca11ee01_address; + constexpr auto callee2 = 0xca11ee02_address; + pre.insert(callee2, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + pre.insert(callee1, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(ret(extdelegatecall(callee2)), 3), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}, {0x02_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + sstore(1, extstaticcall(callee1)) + sstore(2, returndataload(0)) + OP_STOP, 3), + }); + expect.gas_used = 974995; + // Outer call - success. + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + // Inner call - abort. + expect.post[*tx.to].storage[0x02_bytes32] = 0x02_bytes32; + // SSTORE failed. + expect.post[callee1].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[callee2].storage[0x01_bytes32] = 0xdd_bytes32; +} + +TEST_F(state_transition, extcall_static_with_value) +{ + rev = EVMC_PRAGUE; + + constexpr auto callee1 = 0xca11ee01_address; + constexpr auto callee2 = 0xca11ee02_address; + pre.insert(callee1, { + .code = eof_bytecode(extcall(callee2).value(1) + OP_STOP, 4), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = + { + {0x01_bytes32, 0xdd_bytes32}, + }, + .code = eof_bytecode(sstore(1, extstaticcall(callee1)) + OP_STOP, 3), + }); + expect.gas_used = 989747; + // Outer call - abort in callee. + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[callee1].exists = true; +} + +TEST_F(state_transition, extcall_failing_with_value_balance_check) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + sstore(1, extcall(callee).input(0x0, 0xff).value(0x1)) + OP_STOP, 4), + }); + + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].storage[0x01_bytes32] = 0xdd_bytes32; +} + +TEST_F(state_transition, extcall_failing_with_value_additional_cost) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + sstore(1, extcall(callee).input(0x0, 0xff).value(0x1)) + OP_STOP, 4), + }); + // Fails on value transfer additional cost - maximum gas limit that triggers this + tx.gas_limit = 37639 - 1; + + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[callee].storage[0x01_bytes32] = 0xdd_bytes32; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extcall_with_value) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .balance = 0x01, + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + sstore(1, extcall(callee).input(0x0, 0xff).value(0x1)) + OP_STOP, 4), + }); + expect.gas_used = 37845; + + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + expect.post[callee].storage[0x01_bytes32] = 0xcc_bytes32; +} + +TEST_F(state_transition, extcall_min_callee_gas_failure_mode) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_STOP, 0), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extcall(callee)) + OP_STOP, 4), + }); + // Just short of what the caller needs + MIN_RETAINED_GAS + MIN_CALLEE_GAS + tx.gas_limit = 21000 + 4 * 3 + 2600 + 5000 + 2300 - 1; + + // Light failure + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_output) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(0x0a0b_bytes32), 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extcall(callee) + sstore(1, returndatacopy(31, 30, 1) + mload(0)) + OP_STOP, 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x000a_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extdelegatecall_output) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(0x0a0b_bytes32), 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndatacopy(31, 30, 1) + mload(0)) + OP_STOP, + 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x000a_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extstaticcall_output) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(0x0a0b_bytes32), 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extstaticcall(callee) + sstore(1, returndatacopy(31, 30, 1) + mload(0)) + OP_STOP, + 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x000a_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_memory) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee).input(0, 0xFFFFFFFF) + OP_STOP, 4), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extdelegatecall_memory) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert( + *tx.to, { + .code = eof_bytecode(extdelegatecall(callee).input(0, 0xFFFFFFFF) + OP_STOP, 3), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extstaticcall_memory) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert( + *tx.to, { + .code = eof_bytecode(extstaticcall(callee).input(0, 0xFFFFFFFF) + OP_STOP, 3), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extcall_ase_ready_violation) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = + 0x0000000000000000000000010000000000000000000000000000000000000000_bytes32; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee) + OP_STOP, 4), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_ARGUMENT_OUT_OF_RANGE; +} + +TEST_F(state_transition, extdelegatecall_ase_ready_violation) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = + 0x0000000000000000000000010000000000000000000000000000000000000000_bytes32; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extdelegatecall(callee) + OP_STOP, 3), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_ARGUMENT_OUT_OF_RANGE; +} + +TEST_F(state_transition, extstaticcall_ase_ready_violation) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = + 0x0000000000000000000000010000000000000000000000000000000000000000_bytes32; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extstaticcall(callee) + OP_STOP, 3), + }); + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_ARGUMENT_OUT_OF_RANGE; +} + +TEST_F(state_transition, extcall_cold_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee) + OP_STOP, 4), + }); + tx.gas_limit = 21000 + 4 * 3 + 100 + 2500 - 1; + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extdelegatecall_cold_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extdelegatecall(callee) + OP_STOP, 3), + }); + tx.gas_limit = 21000 + 3 * 3 + 100 + 2500 - 1; + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extstaticcall_cold_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extstaticcall(callee) + OP_STOP, 3), + }); + tx.gas_limit = 21000 + 3 * 3 + 100 + 2500 - 1; + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extcall_value_zero_to_nonexistent_account) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extcall(callee).value(0x0)) + OP_STOP, 4), + }); + tx.gas_limit = 21000 + 4 * 3 + 5000 + 2300 + 2600 + 2; + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, extcall_then_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert( + *tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + extcall(callee) + rjump(-3), 4), + }); + // Enough to SSTORE and complete EXTCALL, OOG is sure to be in the infinite loop. + tx.gas_limit = 1'000'000; + + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[callee].storage[0x01_bytes32] = 0xdd_bytes32; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extdelegatecall_then_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + extdelegatecall(callee) + rjump(-3), 3), + }); + // Enough to complete EXTDELEGATECALL, OOG in infinite loop. + tx.gas_limit = 35000; + + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[callee].storage[0x01_bytes32] = 0xdd_bytes32; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extstaticcall_then_oog) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_STOP), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0xcc_bytes32) + extstaticcall(callee) + rjump(-3), 3), + }); + // Enough to complete EXTSTATICCALL, OOG in infinite loop. + tx.gas_limit = 35000; + + expect.gas_used = tx.gas_limit; + expect.post[*tx.to].storage[0x01_bytes32] = 0xdd_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_OUT_OF_GAS; +} + +TEST_F(state_transition, extcall_callee_revert) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(revert(0, 0), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extcall(callee)) + OP_STOP, 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extdelegatecall_callee_revert) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(revert(0, 0), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extdelegatecall(callee)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extstaticcall_callee_revert) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(revert(0, 0), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extstaticcall(callee)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extcall_callee_abort) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_INVALID), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extcall(callee)) + OP_STOP, 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extdelegatecall_callee_abort) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_INVALID), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extdelegatecall(callee)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extstaticcall_callee_abort) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_INVALID), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, extstaticcall(callee)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[callee].exists = true; + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, extcall_input) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(eq(calldataload(0), 0x010203)), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(mstore(0, 0x010203) + extcall(callee).input(0, 32) + + sstore(1, returndataload(0)) + OP_STOP, + 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extdelegatecall_input) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(eq(calldataload(0), 0x010203)), 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(mstore(0, 0x010203) + extdelegatecall(callee).input(0, 32) + + sstore(1, returndataload(0)) + OP_STOP, + 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extstaticcall_input) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(eq(calldataload(0), 0x010203)), 2), + }); + + tx.to = To; + pre.insert( + *tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(mstore(0, 0x010203) + extstaticcall(callee).input(0, 32) + + sstore(1, returndataload(0)) + OP_STOP, + 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_with_value_enough_gas) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, {.balance = 0x1}); + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee).value(1) + OP_POP + OP_STOP, 4), + }); + + constexpr auto callee_and_retained = 5000 + 2300; + // Just enough to ensure callee and retained gas. + tx.gas_limit = 21000 + 4 * 3 + callee_and_retained + 9000 + 2600 + 2; + // Callee and retained gas aren't used + expect.gas_used = tx.gas_limit - callee_and_retained; + expect.post[*tx.to].exists = true; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_with_value_low_gas) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, {.balance = 0x1}); + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee).value(1) + OP_POP + OP_STOP, 4), + }); + + // Not enough to ensure MIN_CALLEE_GAS. + tx.gas_limit = 21000 + 4 * 3 + 9000 + 2600 + 2 - 1; + expect.gas_used = tx.gas_limit; + expect.status = EVMC_OUT_OF_GAS; + expect.post[*tx.to].exists = true; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_recipient_and_code_address) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, + { + .code = eof_bytecode(ret(eq(OP_ADDRESS, callee) + eq(OP_CALLER, To) + OP_AND), 3), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(extcall(callee) + sstore(1, returndataload(0)) + OP_STOP, 4), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extdelegatecall_recipient_and_code_address) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, + { + .code = eof_bytecode(ret(eq(OP_ADDRESS, To) + eq(OP_CALLER, tx.sender) + OP_AND), 3), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndataload(0)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extstaticcall_recipient_and_code_address) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, + { + .code = eof_bytecode(ret(eq(OP_ADDRESS, callee) + eq(OP_CALLER, To) + OP_AND), 3), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(extstaticcall(callee) + sstore(1, returndataload(0)) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[callee].exists = true; +} + + +TEST_F(state_transition, extcall_value) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .balance = 0x0, + .code = eof_bytecode(OP_STOP), + }); + + tx.to = To; + pre.insert(*tx.to, { + .balance = 0x1, + .code = eof_bytecode(extcall(callee).value(0x1) + OP_STOP, 4), + }); + expect.post[*tx.to].exists = true; + expect.post[callee].balance = 1; +} + +TEST_F(state_transition, returndatasize_before_extcall) +{ + rev = EVMC_PRAGUE; + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, returndatasize()) + OP_STOP, 2), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, extdelegatecall_returndatasize) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(ret(0, 0x13), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndatasize()) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x13_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extdelegatecall_returndatasize_abort) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(OP_INVALID), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndatasize()) + OP_STOP, 3), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, returndatacopy) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + + pre.insert(callee, { + .code = eof_bytecode(ret(call_output), 2), + }); + + tx.to = To; + pre.insert(*tx.to, + { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndatacopy(0, 0, 32) + mload(0)) + OP_STOP, + 4), + }); + expect.gas_used = 28654; + expect.post[*tx.to].storage[0x01_bytes32] = call_output; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, returndataload) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + + pre.insert(callee, { + .code = eof_bytecode(ret(call_output), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode( + extdelegatecall(callee) + sstore(1, returndataload(0)) + OP_STOP, 3), + }); + expect.gas_used = 28636; + expect.post[*tx.to].storage[0x01_bytes32] = call_output; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_clears_returndata) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + + pre.insert(callee, { + .code = eof_bytecode(ret(call_output), 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(extcall(callee) + extcall(callee).value(1) + + sstore(1, returndatasize()) + OP_STOP, + 5), + }); + + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + expect.post[callee].exists = true; +} + +TEST_F(state_transition, extcall_gas_refund_propagation) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(sstore(1, 0x00) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .code = eof_bytecode(extcall(callee) + OP_STOP, 4), + }); + expect.gas_used = 21000 + 2600 + 5000 + 6 * 3 - 4800; + expect.post[*tx.to].exists = true; + expect.post[callee].storage[0x01_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, extdelegatecall_gas_refund_propagation) +{ + rev = EVMC_PRAGUE; + constexpr auto callee = 0xca11ee_address; + + pre.insert(callee, { + .code = eof_bytecode(sstore(1, 0x00) + OP_STOP, 2), + }); + + tx.to = To; + pre.insert(*tx.to, { + .storage = {{0x01_bytes32, 0xdd_bytes32}}, + .code = eof_bytecode(extdelegatecall(callee) + OP_STOP, 3), + }); + expect.gas_used = 21000 + 2600 + 5000 + 5 * 3 - 4800; + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + expect.post[callee].exists = true; +} diff --git a/test/unittests/state_transition_eof_create_test.cpp b/test/unittests/state_transition_eof_create_test.cpp new file mode 100644 index 00000000..df3e3284 --- /dev/null +++ b/test/unittests/state_transition_eof_create_test.cpp @@ -0,0 +1,1200 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2024 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +namespace +{ +constexpr bytes32 Salt{0xff}; +} + +TEST_F(state_transition, create_with_eof_initcode) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode init_container = eof_bytecode(OP_INVALID); + const auto factory_code = + mstore(0, push(init_container)) + + // init_container will be left-padded in memory to 32 bytes + sstore(0, create().input(32 - init_container.size(), init_container.size())) + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create_with_eof_initcode_cancun) +{ + rev = EVMC_CANCUN; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode init_container = eof_bytecode(OP_INVALID); + const auto factory_code = + mstore(0, push(init_container)) + + // init_container will be left-padded in memory to 32 bytes + sstore(0, create().input(32 - init_container.size(), init_container.size())) + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // fails by EF execution, nonce bumped. + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create2_with_eof_initcode) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode init_container = eof_bytecode(OP_INVALID); + const auto factory_code = + mstore(0, push(init_container)) + + // init_container will be left-padded in memory to 32 bytes + sstore(0, create2().input(32 - init_container.size(), init_container.size()).salt(Salt)) + + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create2_with_eof_initcode_cancun) +{ + rev = EVMC_CANCUN; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode init_container = eof_bytecode(OP_INVALID); + const auto factory_code = + mstore(0, push(init_container)) + + // init_container will be left-padded in memory to 32 bytes + sstore(0, create2().input(32 - init_container.size(), init_container.size()).salt(Salt)) + + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // fails by EF execution, nonce bumped. + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, creation_tx_deploying_eof) +{ + rev = EVMC_PRAGUE; + + const bytecode deploy_container = eof_bytecode(bytecode(OP_INVALID)); + const auto init_code = mstore(0, push(deploy_container)) + + // deploy_container will be left-padded in memory to 32 bytes + ret(32 - deploy_container.size(), deploy_container.size()); + + tx.data = init_code; + + expect.status = EVMC_CONTRACT_VALIDATION_FAILURE; + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].exists = false; +} + +TEST_F(state_transition, create_deploying_eof) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode deploy_container = eof_bytecode(bytecode(OP_INVALID)); + const auto init_code = mstore(0, push(deploy_container)) + + // deploy_container will be left-padded in memory to 32 bytes + ret(32 - deploy_container.size(), deploy_container.size()); + + const auto factory_code = mstore(0, push(init_code)) + + // init_code will be left-padded in memory to 32 bytes + sstore(0, create().input(32 - init_code.size(), init_code.size())) + + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, create2_deploying_eof) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const bytecode deploy_container = eof_bytecode(bytecode(OP_INVALID)); + const auto init_code = mstore(0, push(deploy_container)) + + // deploy_container will be left-padded in memory to 32 bytes + ret(32 - deploy_container.size(), deploy_container.size()); + + const auto factory_code = + mstore(0, push(init_code)) + + // init_code will be left-padded in memory to 32 bytes + sstore(0, create2().input(32 - init_code.size(), init_code.size()).salt((Salt))) + + sstore(1, 1); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, eofcreate_empty_auxdata) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = eofcreate().container(0).input(0, 0).salt(Salt) + ret_top(); + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_auxdata_equal_to_declared) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); + const auto deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = calldatacopy(0, 0, OP_CALLDATASIZE) + + eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt) + + ret_top(); + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = aux_data; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + const auto expected_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_auxdata_longer_than_declared) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto aux_data1 = "aabbccdd"_hex; + const auto aux_data2 = "eeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data1.size()); + const auto deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = calldatacopy(0, 0, OP_CALLDATASIZE) + + eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt) + + ret_top(); + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = aux_data1 + aux_data2; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + const auto expected_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data1 + aux_data2); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_auxdata_shorter_than_declared) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size() + 1); + const auto deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const auto init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = aux_data; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_dataloadn_referring_to_auxdata) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = bytes(64, 0); + const auto aux_data = bytes(32, 0); + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); + // DATALOADN{64} - referring to data that will be appended as aux_data + const auto deploy_code = bytecode(OP_DATALOADN) + "0040" + ret_top(); + const auto deploy_container = eof_bytecode(deploy_code, 2).data(deploy_data, deploy_data_size); + + const auto init_code = returncontract(0, 0, 32); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = + sstore(0, eofcreate().container(0).input(0, 0).salt(Salt)) + sstore(1, 1) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + const auto expected_container = eof_bytecode(deploy_code, 2).data(deploy_data + aux_data); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_with_auxdata_and_subcontainer) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); + const auto deploy_container = eof_bytecode(eofcreate() + OP_STOP, 4) + .container(eof_bytecode(OP_INVALID)) + .data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + sstore(1, 1) + + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = aux_data; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + const auto expected_container = eof_bytecode(eofcreate() + OP_STOP, 4) + .container(eof_bytecode(OP_INVALID)) + .data(deploy_data + aux_data); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_revert_empty_returndata) +{ + rev = EVMC_PRAGUE; + const auto init_code = revert(0, 0); + const auto init_container = eof_bytecode(init_code, 2); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + + sstore(1, OP_RETURNDATASIZE) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_revert_non_empty_returndata) +{ + rev = EVMC_PRAGUE; + const auto init_code = mstore8(0, 0xaa) + revert(0, 1); + const auto init_container = eof_bytecode(init_code, 2); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + + sstore(1, OP_RETURNDATASIZE) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, eofcreate_initcontainer_aborts) +{ + rev = EVMC_PRAGUE; + const auto init_code = bytecode{Opcode{OP_INVALID}}; + const auto init_container = eof_bytecode(init_code, 0); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_deploy_container_max_size) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto eof_header_size = + static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); + const auto deploy_code = (0x5fff - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; + const bytecode deploy_container = eof_bytecode(deploy_code); + EXPECT_EQ(deploy_container.size(), 0x6000); + + // no aux data + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[create_address].code = deploy_container; +} + +TEST_F(state_transition, eofcreate_deploy_container_too_large) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto eof_header_size = + static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); + const auto deploy_code = (0x6000 - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; + const bytecode deploy_container = eof_bytecode(deploy_code); + EXPECT_EQ(deploy_container.size(), 0x6001); + + // no aux data + const auto init_code = returncontract(0, 0, 0); + const auto init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_appended_data_size_larger_than_64K) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto aux_data = bytes(std::numeric_limits::max(), 0); + const auto deploy_data = "aa"_hex; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + static constexpr bytes32 salt2{0xfe}; + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + // with aux data, final data size = 2**16 + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + + // no aux_data - final data size = 1 + sstore(1, eofcreate().container(0).salt(salt2)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = aux_data; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 2; // 1 successful creation + 1 hard fail + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + const auto create_address = compute_create2_address(*tx.to, salt2, init_container); + expect.post[*tx.to].storage[0x01_bytes32] = to_bytes32(create_address); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_deploy_container_with_aux_data_too_large) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto eof_header_size = + static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); + const auto deploy_code = (0x5fff - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; + const bytecode deploy_container = eof_bytecode(deploy_code); + EXPECT_EQ(deploy_container.size(), 0x6000); + + // 1 byte aux data + const auto init_code = returncontract(0, 0, 1); + const auto init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_nested_eofcreate) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); + + const auto deploy_data_nested = "ffffff"_hex; + const auto deploy_container_nested = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data_nested); + + const auto init_code_nested = returncontract(0, 0, 0); + const bytecode init_container_nested = + eof_bytecode(init_code_nested, 2).container(deploy_container_nested); + + const auto init_code = sstore(0, eofcreate().container(1).salt(Salt)) + returncontract(0, 0, 0); + const bytecode init_container = + eof_bytecode(init_code, 4).container(deploy_container).container(init_container_nested); + + const auto factory_code = sstore(0, eofcreate().container(0).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 2; + const auto create_address_nested = + compute_create2_address(create_address, Salt, init_container_nested); + expect.post[create_address].storage[0x00_bytes32] = to_bytes32(create_address_nested); + expect.post[create_address_nested].code = deploy_container_nested; + expect.post[create_address_nested].nonce = 1; +} + +TEST_F(state_transition, eofcreate_nested_eofcreate_revert) +{ + rev = EVMC_PRAGUE; + + const auto deploy_data_nested = "ffffff"_hex; + const auto deploy_container_nested = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data_nested); + + const auto init_code_nested = returncontract(0, 0, 0); + const auto init_container_nested = + eof_bytecode(init_code_nested, 2).container(deploy_container_nested); + + const auto init_code = sstore(0, eofcreate().container(0).salt(Salt)) + revert(0, 0); + const auto init_container = eof_bytecode(init_code, 4).container(init_container_nested); + + const auto factory_code = sstore(0, eofcreate().container(0).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_caller_balance_too_low) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto deploy_container = eof_bytecode(bytecode{Opcode{OP_INVALID}}).data(deploy_data); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const auto init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt).value(10)) + + sstore(1, 1) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x01_bytes32; +} + +TEST_F(state_transition, eofcreate_not_enough_gas_for_initcode_charge) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + auto init_container = eof_bytecode(init_code, 2).container(deploy_container); + const uint16_t init_data_size = std::numeric_limits::max() / 2 - + static_cast(bytecode(init_container).size()); + const auto init_data = bytes(init_data_size, 0); + init_container.data(init_data, init_data_size); + EXPECT_EQ(bytecode(init_container).size(), std::numeric_limits::max() / 2); + + const auto factory_code = sstore(0, eofcreate().container(0).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + // tx intrinsic cost + EOFCREATE cost + initcode charge - not enough for pushes before EOFCREATE + tx.gas_limit = 21'000 + 32'000 + (std::numeric_limits::max() / 2 + 31) / 32 * 6; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.status = EVMC_OUT_OF_GAS; + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_not_enough_gas_for_mem_expansion) +{ + rev = EVMC_PRAGUE; + auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + // max size aux data + const auto aux_data_size = static_cast( + std::numeric_limits::max() - bytecode(deploy_container).size()); + deploy_container.data({}, aux_data_size); + EXPECT_EQ( + bytecode(deploy_container).size() + aux_data_size, std::numeric_limits::max()); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto factory_code = + sstore(0, eofcreate().container(0).input(0, aux_data_size).salt(Salt)) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + // tx intrinsic cost + EOFCREATE cost + initcode charge + mem expansion size = not enough for + // pushes before EOFCREATE + const auto initcode_size_words = (init_container.size() + 31) / 32; + const auto aux_data_size_words = (aux_data_size + 31) / 32; + tx.gas_limit = 21'000 + 32'000 + static_cast(6 * initcode_size_words) + + 3 * aux_data_size_words + aux_data_size_words * aux_data_size_words / 512; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.status = EVMC_OUT_OF_GAS; + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, returncontract_not_enough_gas_for_mem_expansion) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + // max size aux data + const auto aux_data_size = static_cast( + std::numeric_limits::max() - bytecode(deploy_container).size()); + deploy_container.data({}, aux_data_size); + EXPECT_EQ( + bytecode(deploy_container).size() + aux_data_size, std::numeric_limits::max()); + + const auto init_code = returncontract(0, 0, aux_data_size); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = eofcreate().container(0).salt(Salt) + OP_POP + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + // tx intrinsic cost + EOFCREATE cost + initcode charge + mem expansion size = not enough for + // pushes before RETURNDATALOAD + const auto initcode_size_words = (init_container.size() + 31) / 32; + const auto aux_data_size_words = (aux_data_size + 31) / 32; + tx.gas_limit = 21'000 + 32'000 + static_cast(6 * initcode_size_words) + + 3 * aux_data_size_words + aux_data_size_words * aux_data_size_words / 512; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; +} + +TEST_F(state_transition, eofcreate_clears_returndata) +{ + static constexpr auto returning_address = 0x3000_address; + + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(OP_STOP); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = sstore(0, extcall(returning_address)) + sstore(1, returndatasize()) + + sstore(2, eofcreate().container(0).salt(Salt)) + + sstore(3, returndatasize()) + sstore(4, 1) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + const auto returning_code = ret(0, 10); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + pre.insert(returning_address, {.nonce = 1, .code = returning_code}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x01_bytes32] = 0x0a_bytes32; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x02_bytes32] = to_bytes32(create_address); + expect.post[*tx.to].storage[0x03_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x04_bytes32] = 0x01_bytes32; + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; + expect.post[returning_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_failure_after_eofcreate_success) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto deploy_container = eof_bytecode(OP_STOP); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + const auto factory_code = sstore(0, eofcreate().container(0).salt(Salt)) + + sstore(1, eofcreate().container(0).salt(Salt)) + // address collision + sstore(2, returndatasize()) + sstore(3, 1) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 2; + const auto create_address = compute_create2_address(*tx.to, Salt, init_container); + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x02_bytes32] = 0x00_bytes32; + expect.post[*tx.to].storage[0x03_bytes32] = 0x01_bytes32; + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, eofcreate_call_created_contract) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; // 3 bytes + const auto static_aux_data = + "aabbccdd00000000000000000000000000000000000000000000000000000000"_hex; // 32 bytes + const auto dynamic_aux_data = "eeff"_hex; // 2 bytes + const auto deploy_data_size = + static_cast(deploy_data.size() + static_aux_data.size()); + const auto deploy_code = rjumpv({6, 12}, calldataload(0)) + // jump to one of 3 cases + 35 + OP_DATALOAD + rjump(9) + // read dynamic aux data + OP_DATALOADN + "0000" + rjump(3) + // read pre_deploy_data_section + OP_DATALOADN + "0003" + // read static aux data + ret_top(); + const auto deploy_container = eof_bytecode(deploy_code, 2).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + const auto create_address = compute_create2_address(To, Salt, init_container); + + const auto factory_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + + sstore(0, eofcreate().container(0).input(0, OP_CALLDATASIZE).salt(Salt)) + + mcopy(0, OP_CALLDATASIZE, 32) + // zero out first 32-byte word of memory + extcall(create_address).input(0, 1) + OP_POP + // calldata 0 + sstore(1, returndataload(0)) + mstore8(31, 1) + + extcall(create_address).input(0, 32) + // calldata 1 + OP_POP + sstore(2, returndataload(0)) + mstore8(31, 2) + + extcall(create_address).input(0, 32) + // calldata 2 + OP_POP + sstore(3, returndataload(0)) + sstore(4, 1) + OP_STOP; + const auto factory_container = eof_bytecode(factory_code, 4).container(init_container); + + tx.to = To; + + tx.data = static_aux_data + dynamic_aux_data; + + pre.insert(*tx.to, {.nonce = 1, .code = factory_container}); + + expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; + expect.post[*tx.to].storage[0x00_bytes32] = to_bytes32(create_address); + expect.post[*tx.to].storage[0x01_bytes32] = + 0xabcdefaabbccdd00000000000000000000000000000000000000000000000000_bytes32; + evmc::bytes32 static_aux_data_32; + std::copy_n(static_aux_data.data(), static_aux_data.size(), &static_aux_data_32.bytes[0]); + expect.post[*tx.to].storage[0x02_bytes32] = static_aux_data_32; + evmc::bytes32 dynamic_aux_data_32; + std::copy_n(dynamic_aux_data.data(), dynamic_aux_data.size(), &dynamic_aux_data_32.bytes[0]); + expect.post[*tx.to].storage[0x03_bytes32] = dynamic_aux_data_32; + expect.post[*tx.to].storage[0x04_bytes32] = 0x01_bytes32; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_deploy_data) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_static_auxdata_in_calldata) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + // aux_data will be appended as calldata to the creation tx input, and later appended to the + // deployed contract's data section on RETURNCONTRACT. + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size()); + const auto aux_data_size = static_cast(aux_data.size()); + + // aux_data_size included in the declared data section size - static data. + const auto deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size + aux_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + tx.data = init_container + bytecode(aux_data); + const auto expected_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data); + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_dynamic_auxdata_in_calldata) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + // aux_data will be appended as calldata to the creation tx input, and later appended the + // deployed contract's data section on RETURNCONTRACT. + const auto aux_data = "aabbccddeeff"_hex; + const auto deploy_data_size = static_cast(deploy_data.size()); + + // aux_data_size not included in the declared data section size - dynamic data. + const auto deploy_container = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + tx.data = init_container + bytecode(aux_data); + const auto expected_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data); + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_dataloadn_referring_to_auxdata) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = bytes(64, 0x01); + const auto aux_data = bytes(32, 0x03); + const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); + // DATALOADN{64} - referring to data that will be appended as aux_data + const auto deploy_code = bytecode(OP_DATALOADN) + "0040" + ret_top(); + const auto deploy_container = eof_bytecode(deploy_code, 2).data(deploy_data, deploy_data_size); + + const auto init_code = + calldatacopy(0, 0, OP_CALLDATASIZE) + returncontract(0, 0, OP_CALLDATASIZE); + const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); + + tx.data = init_container + bytecode(aux_data); + + const auto expected_container = eof_bytecode(deploy_code, 2).data(deploy_data + aux_data); + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = expected_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_initcontainer_aborts) +{ + rev = EVMC_PRAGUE; + const auto init_code = bytecode{Opcode{OP_INVALID}}; + const bytecode init_container = eof_bytecode(init_code, 0); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_INVALID_INSTRUCTION; +} + +TEST_F(state_transition, creation_tx_initcontainer_return) +{ + rev = EVMC_PRAGUE; + const auto init_code = bytecode{0xaa + ret_top()}; + const bytecode init_container = eof_bytecode(init_code, 2); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; +} + +TEST_F(state_transition, creation_tx_initcontainer_stop) +{ + rev = EVMC_PRAGUE; + const auto init_code = bytecode{Opcode{OP_STOP}}; + const bytecode init_container = eof_bytecode(init_code, 0); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; +} + +TEST_F(state_transition, creation_tx_initcontainer_max_size) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container_no_data = eof_bytecode(init_code, 2).container(deploy_container); + const auto data_size = 0xc000 - init_container_no_data.size(); + const bytecode init_container = + eof_bytecode(init_code, 2).container(deploy_container).data(bytes(data_size, 0)); + EXPECT_EQ(init_container.size(), 0xc000); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 1; +} + +TEST_F(state_transition, creation_tx_initcontainer_too_large) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container_no_data = eof_bytecode(init_code, 2).container(deploy_container); + const auto data_size = 0xc001 - init_container_no_data.size(); + const bytecode init_container = + eof_bytecode(init_code, 2).container(deploy_container).data(bytes(data_size, 0)); + EXPECT_EQ(init_container.size(), 0xc001); + + tx.data = init_container; + + expect.tx_error = INIT_CODE_SIZE_LIMIT_EXCEEDED; +} + +TEST_F(state_transition, creation_tx_deploy_container_max_size) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto eof_header_size = + static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); + const auto deploy_code = (0x5fff - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; + const bytecode deploy_container = eof_bytecode(deploy_code); + EXPECT_EQ(deploy_container.size(), 0x6000); + + // no aux data + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = deploy_container; +} + +TEST_F(state_transition, creation_tx_deploy_container_too_large) +{ + rev = EVMC_PRAGUE; + block.gas_limit = 10'000'000; + tx.gas_limit = block.gas_limit; + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; + + const auto eof_header_size = + static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); + const auto deploy_code = (0x6000 - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; + const bytecode deploy_container = eof_bytecode(deploy_code); + EXPECT_EQ(deploy_container.size(), 0x6001); + + // no aux data + const auto init_code = returncontract(0, 0, 0); + const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; +} + +TEST_F(state_transition, creation_tx_nested_eofcreate) +{ + rev = EVMC_PRAGUE; + const auto deploy_data = "abcdef"_hex; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); + + const auto deploy_data_nested = "ffffff"_hex; + const auto deploy_container_nested = + eof_bytecode(bytecode(OP_INVALID)).data(deploy_data_nested); + + const auto init_code_nested = returncontract(0, 0, 0); + const bytecode init_container_nested = + eof_bytecode(init_code_nested, 2).container(deploy_container_nested); + + const auto init_code = sstore(0, eofcreate().container(1).salt(Salt)) + returncontract(0, 0, 0); + const bytecode init_container = + eof_bytecode(init_code, 4).container(deploy_container).container(init_container_nested); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); + expect.post[create_address].code = deploy_container; + expect.post[create_address].nonce = 2; + const auto create_address_nested = + compute_create2_address(create_address, Salt, init_container_nested); + expect.post[create_address].storage[0x00_bytes32] = to_bytes32(create_address_nested); + expect.post[create_address_nested].code = deploy_container_nested; + expect.post[create_address_nested].nonce = 1; +} + +TEST_F(state_transition, creation_tx_invalid_initcode_header) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); + + assert(init_container[3] == 0x01); + init_container[3] = 0x04; // Data section as first section in the header invalid. + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; + expect.gas_used = 53516; +} + +TEST_F(state_transition, creation_tx_invalid_initcode) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + const bytes init_container = + eof_bytecode(init_code, 123).container(deploy_container); // Invalid EOF + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; + expect.gas_used = 53516; +} + +TEST_F(state_transition, creation_tx_truncated_data_initcode) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + const bytes init_container = + eof_bytecode(init_code, 2).data("", 1).container(deploy_container); // Truncated data + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; + expect.gas_used = 53528; +} + +TEST_F(state_transition, creation_tx_invalid_deploycode) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID), 123); // Invalid EOF + + const auto init_code = returncontract(0, 0, 0); + const bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; + expect.gas_used = 53528; +} + +TEST_F(state_transition, creation_tx_invalid_eof_version) +{ + rev = EVMC_PRAGUE; + const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); + + const auto init_code = returncontract(0, 0, 0); + bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); + + assert(init_container[2] == 0x01); + init_container[2] = 0x02; + + tx.data = init_container; + + expect.post[Sender].nonce = pre.get(Sender).nonce + 1; + expect.status = EVMC_FAILURE; + expect.gas_used = 53516; +} diff --git a/test/unittests/state_transition_eof_test.cpp b/test/unittests/state_transition_eof_test.cpp deleted file mode 100644 index fdcef373..00000000 --- a/test/unittests/state_transition_eof_test.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2023 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#include "../utils/bytecode.hpp" -#include "state_transition.hpp" - -using namespace evmc::literals; -using namespace evmone::test; - -TEST_F(state_transition, eof_invalid_initcode) -{ - // TODO: Correction of this address is not verified. - static constexpr auto create_address = 0x864bbda5c698ac34b47a9ea3bd4228802cc5ce3b_address; - - rev = EVMC_CANCUN; - tx.to = To; - pre.insert(*tx.to, - { - .nonce = 1, - .storage = {{0x01_bytes32, {.current = 0x01_bytes32, .original = 0x01_bytes32}}}, - .code = eof1_bytecode(create() + push(1) + OP_SSTORE + OP_STOP, 3), - }); - - EXPECT_EQ(pre.get(tx.sender).balance, 1'000'000'001); // Fixture sanity check. - - expect.gas_used = 985407; - - expect.post[tx.sender].nonce = pre.get(tx.sender).nonce + 1; - expect.post[tx.sender].balance = - pre.get(tx.sender).balance - - (block.base_fee + tx.max_priority_gas_price) * static_cast(*expect.gas_used); - expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // CREATE caller's nonce must be bumped - expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; // CREATE must fail - expect.post[create_address].exists = false; -} diff --git a/test/unittests/state_transition_extcode_test.cpp b/test/unittests/state_transition_extcode_test.cpp new file mode 100644 index 00000000..5e8580b1 --- /dev/null +++ b/test/unittests/state_transition_extcode_test.cpp @@ -0,0 +1,82 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, extcodehash_existent) +{ + rev = EVMC_ISTANBUL; // before account access + block.base_fee = 0; + + static constexpr auto EXT = 0xe4_address; + tx.to = To; + pre.insert(To, {.code = sstore(0, push(EXT) + OP_EXTCODEHASH)}); + pre.insert(EXT, {.code = bytecode{"1234"}}); + + expect.post[EXT].exists = true; + expect.post[To].storage[0x00_bytes32] = keccak256(pre.get(EXT).code); +} + +TEST_F(state_transition, extcodesize_existent) +{ + rev = EVMC_ISTANBUL; // before account access + block.base_fee = 0; + + static constexpr auto EXT = 0xe4_address; + tx.to = To; + pre.insert(To, {.code = sstore(0, push(EXT) + OP_EXTCODESIZE)}); + pre.insert(EXT, {.code = bytes(3, 0)}); + + expect.post[EXT].exists = true; + expect.post[To].storage[0x00_bytes32] = 0x03_bytes32; +} + +constexpr auto target = 0xfffffffffffffffffffffffffffffffffffffffe_address; + +TEST_F(state_transition, legacy_extcodesize_eof) +{ + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(push(target) + sstore(1, OP_EXTCODESIZE)), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[target].exists = true; +} + +TEST_F(state_transition, legacy_extcodehash_eof) +{ + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(push(target) + sstore(1, OP_EXTCODEHASH)), + }); + expect.post[*tx.to].storage[0x01_bytes32] = keccak256(bytecode("EF00")); + expect.post[target].exists = true; +} + +TEST_F(state_transition, legacy_extcodecopy_eof) +{ + constexpr auto ones = + 0x1111111111111111111111111111111111111111111111111111111111111111_bytes32; + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(mstore(0, ones) + push(20) + push0() + push0() + + push(target) + OP_EXTCODECOPY + sstore(1, mload(0))), + }); + expect.post[*tx.to].storage[0x01_bytes32] = + 0xef00000000000000000000000000000000000000111111111111111111111111_bytes32; + expect.post[target].exists = true; +} diff --git a/test/unittests/state_transition_selfdestruct_test.cpp b/test/unittests/state_transition_selfdestruct_test.cpp new file mode 100644 index 00000000..06dc3cc2 --- /dev/null +++ b/test/unittests/state_transition_selfdestruct_test.cpp @@ -0,0 +1,171 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, selfdestruct_shanghai) +{ + rev = EVMC_SHANGHAI; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)}); + + expect.post[To].exists = false; + expect.post[0xbe_address].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_cancun) +{ + rev = EVMC_CANCUN; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)}); + + expect.post[To].balance = 0; + expect.post[0xbe_address].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_to_self_cancun) +{ + rev = EVMC_CANCUN; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(To)}); + + expect.post[To].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_same_tx_cancun) +{ + rev = EVMC_CANCUN; + tx.value = 0x4e; + tx.data = selfdestruct(0xbe_address); + pre.get(Sender).balance += 0x4e; + + expect.post[0xbe_address].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_same_create_cancun) +{ + // Use CREATE to temporarily create an account using initcode with SELFDESTRUCT. + // The CREATE should succeed by returning proper address, but the created account + // should not be in the post state. + rev = EVMC_CANCUN; + static constexpr auto BENEFICIARY = 0x4a0000be_address; + const auto initcode = selfdestruct(BENEFICIARY); + + tx.to = To; + pre[To] = { + .balance = 0x4e, + .code = mstore(0, push(initcode)) + + create().input(32 - initcode.size(), initcode.size()).value(0x0e) + sstore(0), + }; + + expect.post[To].balance = 0x40; + expect.post[To].storage[0x00_bytes32] = to_bytes32(compute_create_address(To, pre[To].nonce)); + expect.post[BENEFICIARY].balance = 0x0e; +} + +TEST_F(state_transition, selfdestruct_beneficiary_with_code) +{ + // Send ETH via SELFDESTRUCT to an account with code. + // This test checks if the beneficiary's code in the state is not somehow disturbed + // by this action as we likely don't load the code from database. + rev = EVMC_CANCUN; + static constexpr auto BENEFICIARY = 0x4a0000be_address; + + tx.to = To; + pre[To] = {.balance = 1, .code = selfdestruct(BENEFICIARY)}; + pre[BENEFICIARY] = {.code = bytecode{OP_STOP}}; + + expect.post[To].balance = 0; + expect.post[BENEFICIARY].code = pre[BENEFICIARY].code; +} + +TEST_F(state_transition, selfdestruct_double_revert) +{ + rev = EVMC_SHANGHAI; + + static constexpr auto CALL_PROXY = 0xc0_address; + static constexpr auto REVERT_PROXY = 0xd0_address; + static constexpr auto SELFDESTRUCT = 0xff_address; + static constexpr auto BENEFICIARY = 0xbe_address; + + pre.insert(SELFDESTRUCT, {.balance = 1, .code = selfdestruct(BENEFICIARY)}); + pre.insert(CALL_PROXY, {.code = call(SELFDESTRUCT).gas(0xffffff)}); + pre.insert(REVERT_PROXY, {.code = call(SELFDESTRUCT).gas(0xffffff) + revert(0, 0)}); + pre.insert(To, {.code = call(CALL_PROXY).gas(0xffffff) + call(REVERT_PROXY).gas(0xffffff)}); + tx.to = To; + + expect.post[SELFDESTRUCT].exists = false; + expect.post[CALL_PROXY].exists = true; + expect.post[REVERT_PROXY].exists = true; + expect.post[To].exists = true; + expect.post[BENEFICIARY].balance = 1; +} + +TEST_F(state_transition, selfdestruct_initcode) +{ + tx.data = selfdestruct(0xbe_address); +} + +TEST_F(state_transition, massdestruct_shanghai) +{ + rev = EVMC_SHANGHAI; + + static constexpr auto BASE = 0xdead0000_address; + static constexpr auto SINK = 0xbeef_address; + static constexpr size_t N = 3930; + + const auto b = intx::be::load(BASE); + const auto selfdestruct_code = selfdestruct(SINK); + bytecode driver_code; + for (size_t i = 0; i < N; ++i) + { + const auto a = intx::be::trunc
(b + i); + pre.insert(a, {.balance = 1, .code = selfdestruct_code}); + driver_code += 5 * OP_PUSH0 + push(a) + OP_DUP1 + OP_CALL + OP_POP; + } + + tx.to = To; + tx.gas_limit = 30'000'000; + block.gas_limit = tx.gas_limit; + + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price; + pre.insert(*tx.to, {.code = driver_code}); + expect.post[*tx.to].exists = true; + + expect.post[SINK].balance = N; +} + +TEST_F(state_transition, massdestruct_cancun) +{ + rev = EVMC_CANCUN; + + static constexpr auto BASE = 0xdead0000_address; + static constexpr auto SINK = 0xbeef_address; + static constexpr size_t N = 3930; + + const auto b = intx::be::load(BASE); + const auto selfdestruct_code = selfdestruct(SINK); + bytecode driver_code; + for (size_t i = 0; i < N; ++i) + { + const auto a = intx::be::trunc
(b + i); + pre.insert(a, {.balance = 1, .code = selfdestruct_code}); + driver_code += 5 * OP_PUSH0 + push(a) + OP_DUP1 + OP_CALL + OP_POP; + expect.post[a].balance = 0; + } + + tx.to = To; + tx.gas_limit = 30'000'000; + block.gas_limit = tx.gas_limit; + + pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price; + pre.insert(*tx.to, {.code = driver_code}); + expect.post[*tx.to].exists = true; + + expect.post[SINK].balance = N; +} diff --git a/test/unittests/state_transition_touch_test.cpp b/test/unittests/state_transition_touch_test.cpp new file mode 100644 index 00000000..6d277fcc --- /dev/null +++ b/test/unittests/state_transition_touch_test.cpp @@ -0,0 +1,213 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, touch_empty_sd) +{ + rev = EVMC_SPURIOUS_DRAGON; // touching enabled + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY)}); + pre.insert(EMPTY, {}); + + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = false; +} + +TEST_F(state_transition, touch_empty_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY)}); + pre.insert(EMPTY, {}); + + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = true; +} + +TEST_F(state_transition, touch_nonexistent_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto NONEXISTENT = 0x4e_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(NONEXISTENT)}); + + expect.post[*tx.to].exists = true; + expect.post[NONEXISTENT].exists = true; +} + +TEST_F(state_transition, touch_nonexistent_sd) +{ + rev = EVMC_SPURIOUS_DRAGON; + block.base_fee = 0; + static constexpr auto NONEXISTENT = 0x4e_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(NONEXISTENT)}); + + expect.post[*tx.to].exists = true; +} + +TEST_F(state_transition, touch_nonempty_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto WITH_BALANCE = 0xba_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(WITH_BALANCE)}); + pre.insert(WITH_BALANCE, {.balance = 1}); + + expect.post[*tx.to].exists = true; + expect.post[WITH_BALANCE].exists = true; +} + +TEST_F(state_transition, touch_revert_empty) +{ + rev = EVMC_ISTANBUL; // avoid handling account access (Berlin) + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY) + revert(0, 0)}); + pre.insert(EMPTY, {}); + + expect.status = EVMC_REVERT; + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = true; +} + +TEST_F(state_transition, touch_revert_nonexistent_istanbul) +{ + rev = EVMC_ISTANBUL; // avoid handling account access (Berlin) + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY) + revert(0, 0)}); + + expect.status = EVMC_REVERT; + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = false; +} + +TEST_F(state_transition, touch_revert_nonexistent_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY) + OP_INVALID}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = false; +} + +TEST_F(state_transition, touch_revert_nonempty_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto WITH_BALANCE = 0xba_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(WITH_BALANCE) + OP_INVALID}); + pre.insert(WITH_BALANCE, {.balance = 1}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].exists = true; + expect.post[WITH_BALANCE].exists = true; +} + +TEST_F(state_transition, touch_revert_nonexistent_touch_again_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + static constexpr auto REVERT_PROXY = 0x94_address; + + tx.to = To; + pre.insert(REVERT_PROXY, {.code = call(EMPTY) + OP_INVALID}); + pre.insert(*tx.to, {.code = call(REVERT_PROXY).gas(0xffff) + call(EMPTY)}); + + expect.post[*tx.to].exists = true; + expect.post[REVERT_PROXY].exists = true; + expect.post[EMPTY].exists = true; +} + +TEST_F(state_transition, touch_touch_revert_nonexistent_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + static constexpr auto REVERT_PROXY = 0x94_address; + + tx.to = To; + pre.insert(REVERT_PROXY, {.code = call(EMPTY) + OP_INVALID}); + pre.insert(*tx.to, {.code = call(EMPTY) + call(REVERT_PROXY).gas(0xffff)}); + + expect.post[*tx.to].exists = true; + expect.post[REVERT_PROXY].exists = true; + expect.post[EMPTY].exists = true; +} + +TEST_F(state_transition, touch_revert_touch_revert_nonexistent_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + static constexpr auto REVERT_PROXY = 0x94_address; + + tx.to = To; + pre.insert(REVERT_PROXY, {.code = call(EMPTY) + OP_INVALID}); + pre.insert(*tx.to, {.code = 2 * call(REVERT_PROXY).gas(0xffff)}); + + expect.post[*tx.to].exists = true; + expect.post[REVERT_PROXY].exists = true; + expect.post[EMPTY].exists = false; +} + +TEST_F(state_transition, touch_touch_revert_nonexistent_tw_2) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto EMPTY = 0xee_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(EMPTY) + call(EMPTY) + OP_INVALID}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].exists = true; + expect.post[EMPTY].exists = false; +} + +TEST_F(state_transition, touch_revert_selfdestruct_to_nonexistient_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; // no touching + block.base_fee = 0; + static constexpr auto DESTRUCTOR = 0xde_address; + static constexpr auto BENEFICIARY = 0xbe_address; + + tx.to = To; + pre.insert(*tx.to, {.code = call(DESTRUCTOR).gas(0xffff) + OP_INVALID}); + pre.insert(DESTRUCTOR, {.code = selfdestruct(BENEFICIARY)}); + + expect.status = EVMC_INVALID_INSTRUCTION; + expect.post[*tx.to].exists = true; + expect.post[DESTRUCTOR].exists = true; + expect.post[BENEFICIARY].exists = false; +} diff --git a/test/unittests/state_transition_trace_test.cpp b/test/unittests/state_transition_trace_test.cpp new file mode 100644 index 00000000..0b3de6b1 --- /dev/null +++ b/test/unittests/state_transition_trace_test.cpp @@ -0,0 +1,24 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "state_transition.hpp" +#include + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, trace_example) +{ + tx.to = To; + pre.insert(To, {.code = revert(0, 1)}); + + expect.status = EVMC_REVERT; + expect.post[To].exists = true; + + expect.trace = R"( +{"pc":0,"op":96,"gas":"0xef038","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xef035","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":253,"gas":"0xef032","gasCost":"0x0","memSize":0,"stack":["0x1","0x0"],"depth":1,"refund":0,"opName":"REVERT"} +)"; +} diff --git a/test/unittests/state_transition_transient_storage_test.cpp b/test/unittests/state_transition_transient_storage_test.cpp new file mode 100644 index 00000000..ff17a1a8 --- /dev/null +++ b/test/unittests/state_transition_transient_storage_test.cpp @@ -0,0 +1,62 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, transient_storage) +{ + rev = EVMC_CANCUN; + const auto tbump = 0xb0_address; + + tx.to = To; + pre.insert(tbump, {.code = tstore(0, add(tload(0), 1)) + sstore(0, tload(0))}); + pre.insert(*tx.to, {.code = call(tbump).gas(0xffff) + call(tbump).gas(0xffff)}); + + expect.post[To].exists = true; + expect.post[tbump].storage[0x00_bytes32] = 0x02_bytes32; +} + +TEST_F(state_transition, transient_storage_revert) +{ + rev = EVMC_CANCUN; + const auto tbump = 0xb0_address; + + tx.to = To; + pre.insert(tbump, {.code = tstore(0, add(tload(0), 1)) + sstore(0, tload(0))}); + pre.insert(*tx.to, + {.code = call(tbump).gas(0xffff) + call(tbump).gas(0x1ff) + call(tbump).gas(0xffff)}); + + expect.post[To].exists = true; + expect.post[tbump].storage[0x00_bytes32] = 0x02_bytes32; +} + +TEST_F(state_transition, transient_storage_static) +{ + rev = EVMC_CANCUN; + const auto db = 0xdb_address; + + tx.to = To; + pre.insert(db, {.code = tload(1) + jumpi(17, calldataload(0)) + ret_top() + OP_JUMPDEST + + tstore(1, add(7))}); + pre.insert(*tx.to, {.code = mstore(0, 1) + + // bump db.tstore[1] += 7 + sstore(0xc1, call(db).gas(0xffff).input(0, 32)) + + // get db.tstore[1] + sstore(0xc2, staticcall(db).gas(0xffff).output(0, 32)) + + // sstore[0xd1] = db.tstore[1] + sstore(0xd1, mload(0)) + + // static call to bump db.tstore[1] fails + sstore(0xc3, staticcall(db).gas(0xffff).input(0, 32))}); + + expect.post[db].exists = true; + expect.post[To].exists = true; + expect.post[To].storage[0xc1_bytes32] = 0x01_bytes32; + expect.post[To].storage[0xc2_bytes32] = 0x01_bytes32; + expect.post[To].storage[0xc3_bytes32] = 0x00_bytes32; + expect.post[To].storage[0xd1_bytes32] = 0x07_bytes32; +} diff --git a/test/unittests/state_transition_tx_test.cpp b/test/unittests/state_transition_tx_test.cpp new file mode 100644 index 00000000..57f26e5c --- /dev/null +++ b/test/unittests/state_transition_tx_test.cpp @@ -0,0 +1,104 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "state_transition.hpp" +#include + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, tx_legacy) +{ + rev = EVMC_ISTANBUL; + block.base_fee = 0; // should be 0 before London + tx.to = To; + + expect.post.at(Sender).nonce = pre.get(Sender).nonce + 1; +} + +TEST_F(state_transition, tx_non_existing_sender) +{ + rev = EVMC_BERLIN; + block.base_fee = 0; // should be 0 before London + tx.to = To; + tx.max_gas_price = 0; + tx.max_priority_gas_price = 0; + tx.nonce = 0; + pre.erase(Sender); + + expect.status = EVMC_SUCCESS; + expect.post.at(Sender).nonce = 1; + expect.post[Coinbase].exists = false; +} + +TEST_F(state_transition, invalid_tx_non_existing_sender) +{ + rev = EVMC_BERLIN; + block.base_fee = 0; // should be 0 before London + tx.to = To; + tx.max_gas_price = 1; + tx.max_priority_gas_price = 1; + tx.nonce = 0; + pre.erase(Sender); + + expect.tx_error = INSUFFICIENT_FUNDS; + expect.post[Sender].exists = false; +} + +TEST_F(state_transition, tx_blob_gas_price) +{ + rev = EVMC_CANCUN; + tx.to = To; + tx.gas_limit = 25000; + tx.max_gas_price = block.base_fee; // minimal gas price to make it + tx.max_priority_gas_price = 0; + tx.nonce = 1; + tx.type = Transaction::Type::blob; + tx.blob_hashes.emplace_back( + 0x0100000000000000000000000000000000000000000000000000000000000000_bytes32); + tx.max_blob_gas_price = 1; + + pre.get(tx.sender).balance = 0x20000 + tx.gas_limit * tx.max_gas_price; + + expect.post[Coinbase].exists = false; // all gas is burned, Coinbase gets nothing + expect.status = EVMC_SUCCESS; +} + +TEST_F(state_transition, empty_coinbase_fee_0_sd) +{ + rev = EVMC_SPURIOUS_DRAGON; + block_reward = 0; + block.base_fee = 0; // should be 0 before London + tx.max_gas_price = 0; + tx.max_priority_gas_price = 0; + tx.to = To; + pre.insert(Coinbase, {}); + expect.post[To].exists = false; + expect.post[Coinbase].exists = false; +} + +TEST_F(state_transition, empty_coinbase_fee_0_tw) +{ + rev = EVMC_TANGERINE_WHISTLE; + block_reward = 0; + block.base_fee = 0; // should be 0 before London + tx.max_gas_price = 0; + tx.max_priority_gas_price = 0; + tx.to = To; + pre.insert(Coinbase, {}); + expect.post[To].exists = true; + expect.post[Coinbase].balance = 0; +} + +TEST_F(state_transition, access_list_storage) +{ + tx.to = To; + tx.access_list = {{To, {0x01_bytes32}}}; + + pre.insert(To, {.storage = {{0x01_bytes32, 0x01_bytes32}}, .code = sstore(2, sload(1))}); + + expect.post[To].storage[0x01_bytes32] = 0x01_bytes32; + expect.post[To].storage[0x02_bytes32] = 0x01_bytes32; + expect.gas_used = 47506; // Without access list: 45206 +} diff --git a/test/unittests/state_tx_test.cpp b/test/unittests/state_tx_test.cpp new file mode 100644 index 00000000..747303ec --- /dev/null +++ b/test/unittests/state_tx_test.cpp @@ -0,0 +1,185 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +using namespace evmc::literals; +using namespace evmone::state; +using namespace evmone::test; + +TEST(state_tx, validate_nonce) +{ + const BlockInfo bi{.gas_limit = 0x989680, + .coinbase = 0x01_address, + .prev_randao = {}, + .base_fee = 0x0a, + .withdrawals = {}}; + const Account acc{.nonce = 1, .balance = 0xe8d4a51000}; + Transaction tx{ + .data = {}, + .gas_limit = 60000, + .max_gas_price = bi.base_fee, + .max_priority_gas_price = 0, + .sender = 0x02_address, + .to = {}, + .value = 0, + .access_list = {}, + .nonce = 1, + .r = 0, + .s = 0, + }; + + ASSERT_FALSE(holds_alternative( + validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000, 0))); + + tx.nonce = 0; + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000, 0)) + .message(), + "nonce too low"); + + tx.nonce = 2; + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000, 0)) + .message(), + "nonce too high"); +} + +TEST(state_tx, validate_sender) +{ + BlockInfo bi{.gas_limit = 0x989680, + .coinbase = 0x01_address, + .prev_randao = {}, + .base_fee = 0, + .withdrawals = {}}; + const Account acc{.nonce = 0, .balance = 0}; + Transaction tx{ + .data = {}, + .gas_limit = 60000, + .max_gas_price = bi.base_fee, + .max_priority_gas_price = 0, + .sender = 0x02_address, + .to = {}, + .value = 0, + .access_list = {}, + .nonce = 0, + .r = 0, + .s = 0, + }; + + ASSERT_FALSE(holds_alternative( + validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000, 0))); + + bi.base_fee = 1; + + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_LONDON, 60000, 0)) + .message(), + "max fee per gas less than block base fee"); + + tx.max_gas_price = bi.base_fee; + + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_LONDON, 60000, 0)) + .message(), + "insufficient funds for gas * price + value"); +} + +TEST(state_tx, validate_blob_tx) +{ + const BlockInfo bi{.gas_limit = 0x989680, .base_fee = 1, .blob_base_fee = 1}; + const Account acc{.nonce = 0, .balance = 1000000}; + Transaction tx{ + .type = Transaction::Type::blob, + .gas_limit = 60000, + .max_gas_price = bi.base_fee, + .sender = 0x02_address, + .to = {}, + }; + + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_SHANGHAI, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::TX_TYPE_NOT_SUPPORTED).message()); + + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::CREATE_BLOB_TX).message()); + + tx.to = 0x01_address; + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::EMPTY_BLOB_HASHES_LIST).message()); + + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000001_bytes32); + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000002_bytes32); + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000003_bytes32); + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000004_bytes32); + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000005_bytes32); + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000006_bytes32); + + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::FEE_CAP_LESS_THEN_BLOCKS).message()); + + tx.max_blob_gas_price = 1; + tx.blob_hashes.push_back( + 0x0100000000000000000000000000000000000000000000000000000000000007_bytes32); + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::BLOB_GAS_LIMIT_EXCEEDED).message()); + + tx.blob_hashes.pop_back(); + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK - 1)) + .message(), + make_error_code(ErrorCode::BLOB_GAS_LIMIT_EXCEEDED).message()); + + EXPECT_EQ(std::get(validate_transaction( + acc, bi, tx, EVMC_CANCUN, 60000, BlockInfo::MAX_BLOB_GAS_PER_BLOCK)), + 39000); + + tx.blob_hashes[0] = 0x0200000000000000000000000000000000000000000000000000000000000001_bytes32; + EXPECT_EQ(std::get(validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, + BlockInfo::MAX_BLOB_GAS_PER_BLOCK)) + .message(), + make_error_code(ErrorCode::INVALID_BLOB_HASH_VERSION).message()); +} + +TEST(state_tx, validate_data) +{ + const BlockInfo bi{.gas_limit = 0x989680, + .coinbase = 0x01_address, + .prev_randao = {}, + .base_fee = 0x0a, + .withdrawals = {}}; + const Account acc{.nonce = 1, .balance = 0xe8d4a51000}; + const Transaction tx{ + .data = "EF00 01 010004 0200010001 030004 00 00000000 00 AABBCCDD"_hex, + .gas_limit = 60000, + .max_gas_price = bi.base_fee, + .max_priority_gas_price = 0, + .sender = 0x02_address, + .to = {}, + .value = 0, + .access_list = {}, + .nonce = 1, + .r = 0, + .s = 0, + }; + + ASSERT_FALSE(holds_alternative( + validate_transaction(acc, bi, tx, EVMC_CANCUN, 60000, BlockInfo::MAX_BLOB_GAS_PER_BLOCK))); + ASSERT_FALSE(holds_alternative( + validate_transaction(acc, bi, tx, EVMC_PRAGUE, 60000, BlockInfo::MAX_BLOB_GAS_PER_BLOCK))); +} diff --git a/test/unittests/statetest_loader_block_info_test.cpp b/test/unittests/statetest_loader_block_info_test.cpp index 053539e6..6508b7f1 100644 --- a/test/unittests/statetest_loader_block_info_test.cpp +++ b/test/unittests/statetest_loader_block_info_test.cpp @@ -17,7 +17,11 @@ TEST(statetest_loader, block_info) "currentTimestamp": "0", "currentBaseFee": "7", "currentRandom": "0x00", - "withdrawals": [] + "withdrawals": [], + "blockHashes": { + "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", + "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" + } })"; const auto bi = test::from_json(json::json::parse(input)); @@ -27,6 +31,10 @@ TEST(statetest_loader, block_info) EXPECT_EQ(bi.base_fee, 7); EXPECT_EQ(bi.timestamp, 0); EXPECT_EQ(bi.number, 0); + EXPECT_EQ(bi.known_block_hashes.at(0), + 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); + EXPECT_EQ(bi.known_block_hashes.at(1), + 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); } TEST(statetest_loader, block_info_hex) @@ -131,7 +139,7 @@ TEST(statetest_loader, block_info_0_parent_difficulty) "parentBaseFee": "7", "parentGasUsed": "0", "parentGasLimit": "100000000000000000", - "parentTimstamp": "0", + "parentTimestamp": "253", "blockHashes": { "0": "0xc305d826e3784046a7e9d31128ef98d3e96133fe454c16ef630574d967dfdb1a" }, @@ -147,6 +155,7 @@ TEST(statetest_loader, block_info_0_parent_difficulty) EXPECT_EQ(bi.base_fee, 7); EXPECT_EQ(bi.timestamp, 1000); EXPECT_EQ(bi.number, 1); + EXPECT_EQ(bi.parent_timestamp, 253); } TEST(statetest_loader, block_info_0_random) @@ -210,3 +219,120 @@ TEST(statetest_loader, block_info_withdrawals) EXPECT_EQ(bi.withdrawals[1].recipient, 0x0000000000000000000000000000000000000200_address); EXPECT_EQ(bi.withdrawals[1].get_amount(), intx::uint256{0xffffffffffffffff} * 1'000'000'000); } + +TEST(statetest_loader, block_info_ommers) +{ + constexpr std::string_view input = R"({ + "currentCoinbase": "0x1111111111111111111111111111111111111111", + "currentDifficulty": "0x0", + "currentGasLimit": "0x0", + "currentNumber": "0", + "currentTimestamp": "0", + "currentBaseFee": "7", + "currentRandom": "0x00", + "ommers": [ + { + "address": "0x0000000000000000000000000000000000000100", + "delta": 1 + }, + { + "address": "0x0000000000000000000000000000000000000200", + "delta": 4 + } + ], + "withdrawals": [] + })"; + + const auto bi = test::from_json(json::json::parse(input)); + EXPECT_EQ(bi.coinbase, 0x1111111111111111111111111111111111111111_address); + EXPECT_EQ(bi.prev_randao, 0x00_bytes32); + EXPECT_EQ(bi.gas_limit, 0x0); + EXPECT_EQ(bi.base_fee, 7); + EXPECT_EQ(bi.timestamp, 0); + EXPECT_EQ(bi.number, 0); + EXPECT_EQ(bi.withdrawals.size(), 0); + EXPECT_EQ(bi.ommers.size(), 2); + EXPECT_EQ(bi.ommers[0].beneficiary, 0x0000000000000000000000000000000000000100_address); + EXPECT_EQ(bi.ommers[0].delta, 1); + EXPECT_EQ(bi.ommers[1].beneficiary, 0x0000000000000000000000000000000000000200_address); + EXPECT_EQ(bi.ommers[1].delta, 4); +} + +TEST(statetest_loader, block_info_parent_blob_gas) +{ + constexpr std::string_view input = R"({ + "currentCoinbase": "0x1111111111111111111111111111111111111111", + "currentDifficulty": "0x0", + "currentGasLimit": "0x0", + "currentNumber": "0", + "currentTimestamp": "0", + "currentBaseFee": "7", + "currentRandom": "0x00", + "withdrawals": [], + "blockHashes": { + "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", + "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" + }, + "parentExcessBlobGas": "1", + "parentBlobGasUsed": "0x60000" + })"; + + const auto bi = test::from_json(json::json::parse(input)); + EXPECT_EQ(bi.coinbase, 0x1111111111111111111111111111111111111111_address); + EXPECT_EQ(bi.prev_randao, 0x00_bytes32); + EXPECT_EQ(bi.gas_limit, 0x0); + EXPECT_EQ(bi.base_fee, 7); + EXPECT_EQ(bi.timestamp, 0); + EXPECT_EQ(bi.number, 0); + EXPECT_EQ(bi.known_block_hashes.at(0), + 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); + EXPECT_EQ(bi.known_block_hashes.at(1), + 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); + EXPECT_EQ(bi.excess_blob_gas, 1); +} + +TEST(statetest_loader, block_info_current_blob_gas) +{ + constexpr std::string_view input = R"({ + "currentCoinbase": "0x1111111111111111111111111111111111111111", + "currentDifficulty": "0x0", + "currentGasLimit": "0x0", + "currentNumber": "0", + "currentTimestamp": "0", + "currentBaseFee": "7", + "currentRandom": "0x00", + "withdrawals": [], + "blockHashes": { + "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", + "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" + }, + "currentExcessBlobGas": "2" + })"; + + const auto bi = test::from_json(json::json::parse(input)); + EXPECT_EQ(bi.coinbase, 0x1111111111111111111111111111111111111111_address); + EXPECT_EQ(bi.prev_randao, 0x00_bytes32); + EXPECT_EQ(bi.gas_limit, 0x0); + EXPECT_EQ(bi.base_fee, 7); + EXPECT_EQ(bi.timestamp, 0); + EXPECT_EQ(bi.number, 0); + EXPECT_EQ(bi.known_block_hashes.at(0), + 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); + EXPECT_EQ(bi.known_block_hashes.at(1), + 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); + EXPECT_EQ(bi.excess_blob_gas, 2); +} + +TEST(statetest_loader, block_info_parent_beacon_block_root) +{ + constexpr std::string_view input = R"({ + "currentNumber": "0", + "currentTimestamp": "0", + "currentGasLimit": "0", + "currentCoinbase": "", + "parentBeaconBlockRoot": "0xbeac045007" + })"; + + const auto bi = test::from_json(json::json::parse(input)); + EXPECT_EQ(bi.parent_beacon_block_root, 0xbeac045007_bytes32); +} diff --git a/test/unittests/statetest_loader_test.cpp b/test/unittests/statetest_loader_test.cpp index 0f8b724a..c528fdd9 100644 --- a/test/unittests/statetest_loader_test.cpp +++ b/test/unittests/statetest_loader_test.cpp @@ -52,9 +52,16 @@ TEST(json_loader, int64_t) from_json(basic_json("9223372036854775807")), std::numeric_limits::max()); EXPECT_EQ(from_json(basic_json("-9223372036854775808")), std::numeric_limits::min()); - EXPECT_THROW(from_json(basic_json("0xffffffffffffffff")), std::out_of_range); - EXPECT_THROW(from_json(basic_json("9223372036854775808")), std::out_of_range); - EXPECT_THROW(from_json(basic_json("-9223372036854775809")), std::out_of_range); + + // Unfortunate conversion results: + EXPECT_EQ(from_json(basic_json("0xffffffffffffffff")), int64_t{-1}); + EXPECT_EQ( + from_json(basic_json("9223372036854775808")), std::numeric_limits::min()); + EXPECT_EQ(from_json(basic_json("-9223372036854775809")), + std::numeric_limits::max()); + + EXPECT_THROW(from_json(basic_json("0x10000000000000000")), std::out_of_range); + EXPECT_THROW(from_json(basic_json("18446744073709551616")), std::out_of_range); // Octal is also supported. EXPECT_EQ(from_json(basic_json("0777")), 0777); @@ -67,14 +74,39 @@ TEST(json_loader, int64_t) TEST(statetest_loader, load_empty_test) { std::istringstream s{"{}"}; - EXPECT_THROW(load_state_test(s), std::invalid_argument); + EXPECT_EQ(load_state_tests(s).size(), 0); +} + +TEST(statetest_loader, load_multi_test) +{ + std::istringstream s{R"({ + "T1": { + "pre": {}, + "transaction": {"gasPrice": "","sender": "","to": "","data": null, + "gasLimit": "0","value": null,"nonce" : "0"}, + "post": {}, + "env": {"currentNumber": "0","currentTimestamp": "0", + "currentGasLimit": "0","currentCoinbase": ""} + }, + "T2": { + "pre": {}, + "transaction": {"gasPrice": "","sender": "","to": "","data": null, + "gasLimit": "0","value": null,"nonce" : "0"}, + "post": {}, + "env": {"currentNumber": "0","currentTimestamp": "0", + "currentGasLimit": "0","currentCoinbase": ""} + } + })"}; + const auto tests = load_state_tests(s); + ASSERT_EQ(tests.size(), 2); + EXPECT_EQ(tests[0].name, "T1"); + EXPECT_EQ(tests[1].name, "T2"); } TEST(statetest_loader, load_minimal_test) { std::istringstream s{R"({ "test": { - "_info": {}, "pre": {}, "transaction": { "gasPrice": "", @@ -82,7 +114,8 @@ TEST(statetest_loader, load_minimal_test) "to": "", "data": null, "gasLimit": "0", - "value": null + "value": null, + "nonce" : "0" }, "post": {}, "env": { @@ -93,16 +126,16 @@ TEST(statetest_loader, load_minimal_test) } } })"}; - const StateTransitionTest st = load_state_test(s); + const auto st = std::move(load_state_tests(s).at(0)); // TODO: should add some comparison operator to State, BlockInfo, AccessList - EXPECT_EQ(st.pre_state.get_accounts().size(), 0); + EXPECT_EQ(st.pre_state.size(), 0); EXPECT_EQ(st.block.number, 0); EXPECT_EQ(st.block.timestamp, 0); EXPECT_EQ(st.block.gas_limit, 0); EXPECT_EQ(st.block.coinbase, address{}); EXPECT_EQ(st.block.prev_randao, bytes32{}); EXPECT_EQ(st.block.base_fee, 0); - EXPECT_EQ(st.multi_tx.kind, test::TestMultiTransaction::Kind::legacy); + EXPECT_EQ(st.multi_tx.type, test::TestMultiTransaction::Type::legacy); EXPECT_EQ(st.multi_tx.data, bytes{}); EXPECT_EQ(st.multi_tx.gas_limit, 0); EXPECT_EQ(st.multi_tx.max_gas_price, 0); @@ -110,8 +143,9 @@ TEST(statetest_loader, load_minimal_test) EXPECT_EQ(st.multi_tx.sender, address{}); EXPECT_EQ(st.multi_tx.to, std::nullopt); EXPECT_EQ(st.multi_tx.value, 0); + EXPECT_EQ(st.multi_tx.nonce, 0); EXPECT_EQ(st.multi_tx.access_list.size(), 0); - EXPECT_EQ(st.multi_tx.chain_id, 0); + EXPECT_EQ(st.multi_tx.chain_id, 1); EXPECT_EQ(st.multi_tx.nonce, 0); EXPECT_EQ(st.multi_tx.r, 0); EXPECT_EQ(st.multi_tx.s, 0); @@ -125,23 +159,29 @@ TEST(statetest_loader, load_minimal_test) EXPECT_EQ(st.input_labels.size(), 0); } -TEST(statetest_loader, validate_deployed_code_test) +TEST(statetest_loader, validate_state_invalid_eof) +{ + TestState state{{0xadd4_address, {.code = "EF0001010000020001000103000100FEDA"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, + ThrowsMessage( + "EOF container at 0x000000000000000000000000000000000000add4 is invalid: " + "zero_section_size")); +} + +TEST(statetest_loader, validate_state_unexpected_eof) +{ + TestState state{{0xadd4_address, {.code = "EF00"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_CANCUN); }, + ThrowsMessage( + "unexpected code with EOF prefix at 0x000000000000000000000000000000000000add4")); +} + +TEST(statetest_loader, validate_state_zero_storage_slot) { - { - state::State state; - state.insert(0xadd4_address, {.code = "EF0001010000020001000103000100FEDA"_hex}); - EXPECT_THAT([&] { validate_deployed_code(state, EVMC_CANCUN); }, - ThrowsMessage( - "EOF container at 0x000000000000000000000000000000000000add4 is invalid: " - "zero_section_size")); - } - - { - state::State state; - state.insert(0xadd4_address, {.code = "EF00"_hex}); - EXPECT_THAT([&] { validate_deployed_code(state, EVMC_SHANGHAI); }, - ThrowsMessage( - "code at 0x000000000000000000000000000000000000add4 " - "starts with 0xEF00 in Shanghai")); - } + TestState state{{0xadd4_address, {.storage = {{0x01_bytes32, 0x00_bytes32}}}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, + ThrowsMessage( + "account 0x000000000000000000000000000000000000add4 contains invalid zero-value " + "storage entry " + "0x0000000000000000000000000000000000000000000000000000000000000001")); } diff --git a/test/unittests/statetest_loader_tx_test.cpp b/test/unittests/statetest_loader_tx_test.cpp index fddec80c..7cfa6cdd 100644 --- a/test/unittests/statetest_loader_tx_test.cpp +++ b/test/unittests/statetest_loader_tx_test.cpp @@ -7,11 +7,41 @@ #include using namespace evmone; -using namespace intx; +using namespace intx::literals; using namespace testing; // TODO: Add specific test of loading nonce, chainId, r, s, v +TEST(statetest_loader, tx_to_empty_string) +{ + // The "to":"" is correctly parsed as a creation transaction. + constexpr std::string_view input = R"({ + "to": "", + "input": "", "gas": "1", "chainId": "1", "value": "0", "sender": "a0a1", + "gasPrice": "1", "nonce": "0", "v": "1", + "r": "0x1111111111111111111111111111111111111111111111111111111111111111", + "s": "0x2222222222222222222222222222222222222222222222222222222222222222" + })"; + + const auto tx = test::from_json(json::json::parse(input)); + EXPECT_FALSE(tx.to.has_value()); +} + +TEST(statetest_loader, tx_to_null) +{ + // The "to":null is correctly parsed as a creation transaction. + constexpr std::string_view input = R"({ + "to": null, + "input": "", "gas": "1", "chainId": "1", "value": "0", "sender": "a0a1", + "gasPrice": "1", "nonce": "0", "v": "1", + "r": "0x1111111111111111111111111111111111111111111111111111111111111111", + "s": "0x2222222222222222222222222222222222222222222222222222222222222222" + })"; + + const auto tx = test::from_json(json::json::parse(input)); + EXPECT_FALSE(tx.to.has_value()); +} + TEST(statetest_loader, tx_create_legacy) { constexpr std::string_view input = R"({ @@ -20,7 +50,6 @@ TEST(statetest_loader, tx_create_legacy) "chainId": "0x5", "value": "0xe0e1", "sender": "a0a1", - "to": "", "gasPrice": "0x7071", "nonce": "0", "r": "0x1111111111111111111111111111111111111111111111111111111111111111", @@ -29,7 +58,7 @@ TEST(statetest_loader, tx_create_legacy) })"; const auto tx = test::from_json(json::json::parse(input)); - EXPECT_EQ(tx.kind, state::Transaction::Kind::legacy); + EXPECT_EQ(tx.type, state::Transaction::Type::legacy); EXPECT_EQ(tx.data, (bytes{0xb0, 0xb1})); EXPECT_EQ(tx.gas_limit, 0x9091); EXPECT_EQ(tx.chain_id, 5); @@ -63,10 +92,10 @@ TEST(statetest_loader, tx_eip1559) })"; const auto tx = test::from_json(json::json::parse(input)); - EXPECT_EQ(tx.kind, state::Transaction::Kind::eip1559); + EXPECT_EQ(tx.type, state::Transaction::Type::eip1559); EXPECT_EQ(tx.data, (bytes{0xb0, 0xb1})); EXPECT_EQ(tx.gas_limit, 0x9091); - EXPECT_EQ(tx.chain_id, 0); + EXPECT_EQ(tx.chain_id, 1); EXPECT_EQ(tx.value, 0xe0e1); EXPECT_EQ(tx.sender, 0xa0a1_address); EXPECT_EQ(tx.to, 0xc0c1_address); @@ -86,7 +115,6 @@ TEST(statetest_loader, tx_access_list) "gas": "0", "value": "0", "sender": "", - "to": "", "maxFeePerGas": "0", "maxPriorityFeePerGas": "0", "accessList": [ @@ -100,7 +128,7 @@ TEST(statetest_loader, tx_access_list) })"; const auto tx = test::from_json(json::json::parse(input)); - EXPECT_EQ(tx.kind, state::Transaction::Kind::eip1559); + EXPECT_EQ(tx.type, state::Transaction::Type::eip1559); EXPECT_TRUE(tx.data.empty()); EXPECT_EQ(tx.gas_limit, 0); EXPECT_EQ(tx.value, 0); @@ -150,7 +178,6 @@ TEST(statetest_loader, tx_type_1) "type": "1", "value": "0", "sender": "", - "to": "", "gasPrice": "0", "accessList": [ {"address": "ac01", "storageKeys": []}, @@ -163,7 +190,52 @@ TEST(statetest_loader, tx_type_1) })"; const auto tx = test::from_json(json::json::parse(input)); - EXPECT_EQ(tx.kind, state::Transaction::Kind::eip2930); + EXPECT_EQ(tx.type, state::Transaction::Type::access_list); + EXPECT_TRUE(tx.data.empty()); + EXPECT_EQ(tx.gas_limit, 0); + EXPECT_EQ(tx.value, 0); + EXPECT_EQ(tx.sender, 0x00_address); + EXPECT_FALSE(tx.to.has_value()); + EXPECT_EQ(tx.max_gas_price, 0); + EXPECT_EQ(tx.max_priority_gas_price, 0); + ASSERT_EQ(tx.access_list.size(), 2); + EXPECT_EQ(tx.access_list[0].first, 0xac01_address); + EXPECT_EQ(tx.access_list[0].second.size(), 0); + EXPECT_EQ(tx.access_list[1].first, 0xac02_address); + EXPECT_EQ(tx.access_list[1].second, (std::vector{0xfe_bytes32, 0x00_bytes32})); + EXPECT_EQ(tx.nonce, 0); + EXPECT_EQ(tx.r, 0x1111111111111111111111111111111111111111111111111111111111111111_u256); + EXPECT_EQ(tx.s, 0x2222222222222222222222222222222222222222222222222222222222222222_u256); + EXPECT_EQ(tx.v, 1); +} + +TEST(statetest_loader, tx_type_3) +{ + constexpr std::string_view input = R"({ + "input": "", + "gas": "0", + "type": "3", + "value": "0", + "sender": "", + "maxFeePerGas": "0", + "maxPriorityFeePerGas": "0", + "accessList": [ + {"address": "ac01", "storageKeys": []}, + {"address": "ac02", "storageKeys": ["fe", "00"]} + ], + "maxFeePerBlobGas": "1", + "blobVersionedHashes": [ + "0x0111111111111111111111111111111111111111111111111111111111111111", + "0x0222222222222222222222222222222222222222222222222222222222222222" + ], + "nonce": "0", + "r": "0x1111111111111111111111111111111111111111111111111111111111111111", + "s": "0x2222222222222222222222222222222222222222222222222222222222222222", + "v": "1" + })"; + + const auto tx = test::from_json(json::json::parse(input)); + EXPECT_EQ(tx.type, state::Transaction::Type::blob); EXPECT_TRUE(tx.data.empty()); EXPECT_EQ(tx.gas_limit, 0); EXPECT_EQ(tx.value, 0); @@ -180,6 +252,49 @@ TEST(statetest_loader, tx_type_1) EXPECT_EQ(tx.r, 0x1111111111111111111111111111111111111111111111111111111111111111_u256); EXPECT_EQ(tx.s, 0x2222222222222222222222222222222222222222222222222222222222222222_u256); EXPECT_EQ(tx.v, 1); + EXPECT_EQ(tx.max_blob_gas_price, 1); + EXPECT_EQ(tx.blob_hashes.size(), 2); + EXPECT_EQ(tx.blob_hashes[0], + 0x0111111111111111111111111111111111111111111111111111111111111111_bytes32); +} + +TEST(statetest_loader, tx_invalid_blob_versioned_hash) +{ + constexpr std::string_view input = R"({ + "input" : "0x00", + "gas" : "0x3d0900", + "nonce" : "0x0", + "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0x186a0", + "v" : "0x0", + "r" : "0xbf751ed5c37bd65d3ace5b73a1c62f7388b203a82ce366392e7b76fd2de12cb1", + "s" : "0x6f2b5344e5b997d35b3a0768006196a65c4eff8ed3acad5201a105c2b59b4e8c", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "chainId" : "0x1", + "type" : "0x3", + "maxFeePerGas" : "0x12a05f200", + "maxPriorityFeePerGas" : "0x2", + "accessList" : [ + { + "address" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + "storageKeys" : [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ] + } + ], + "maxFeePerBlobGas" : "0xa", + "blobVersionedHashes" : [ + "0x1a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "0x1a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + ], + "hash" : "0x6f26856255f46b27b31d06e00750d8d75067fd8a28e15e8f5557a33fba288cb5", + "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + })"; + + EXPECT_THAT([&] { test::from_json(json::json::parse(input)); }, + ThrowsMessage( + "invalid hash: 0x1a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")); } TEST(statetest_loader, invalid_tx_type) @@ -191,7 +306,6 @@ TEST(statetest_loader, invalid_tx_type) "type": "2", "value": "0", "sender": "", - "to": "", "gasPrice": "0", "accessList": [ {"address": "ac01", "storageKeys": []}, @@ -204,7 +318,7 @@ TEST(statetest_loader, invalid_tx_type) })"; EXPECT_THAT([&] { test::from_json(json::json::parse(input)); }, - ThrowsMessage("wrong transaction type")); + ThrowsMessage("wrong transaction type: 2, expected: 1")); } { constexpr std::string_view input = R"({ @@ -213,7 +327,6 @@ TEST(statetest_loader, invalid_tx_type) "type": "1", "value": "0", "sender": "", - "to": "", "gasPrice": "0", "nonce": "0", "r": "0x1111111111111111111111111111111111111111111111111111111111111111", @@ -222,7 +335,7 @@ TEST(statetest_loader, invalid_tx_type) })"; EXPECT_THAT([&] { test::from_json(json::json::parse(input)); }, - ThrowsMessage("wrong transaction type")); + ThrowsMessage("wrong transaction type: 1, expected: 0")); } { @@ -232,7 +345,6 @@ TEST(statetest_loader, invalid_tx_type) "type": "1", "value": "0", "sender": "", - "to": "", "maxFeePerGas": "0", "maxPriorityFeePerGas": "0", "accessList": [ @@ -246,7 +358,7 @@ TEST(statetest_loader, invalid_tx_type) })"; EXPECT_THAT([&] { test::from_json(json::json::parse(input)); }, - ThrowsMessage("wrong transaction type")); + ThrowsMessage("wrong transaction type: 1, expected: 2")); } } diff --git a/test/unittests/statetest_logs_hash_test.cpp b/test/unittests/statetest_logs_hash_test.cpp index 2445066b..b32e5cc6 100644 --- a/test/unittests/statetest_logs_hash_test.cpp +++ b/test/unittests/statetest_logs_hash_test.cpp @@ -6,21 +6,18 @@ #include using namespace evmone; - -static constexpr auto EmptyLogsHash = - 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347_bytes32; +using namespace evmone::state; TEST(statetest_logs_hash, empty_logs) { - EXPECT_EQ(test::logs_hash({}), EmptyLogsHash); - EXPECT_EQ(keccak256(bytes{0xc0}), EmptyLogsHash); // Hash of empty RLP list: 0xc0. + EXPECT_EQ(test::logs_hash({}), EmptyListHash); } TEST(statetest_logs_hash, example1) { - const std::vector logs{ - state::Log{0x00_address, bytes{0xb0, 0xb1}, {}}, - state::Log{0xaa_address, {}, {0x01_bytes32, 0x02_bytes32}}, + const std::vector logs{ + Log{0x00_address, bytes{0xb0, 0xb1}, {}}, + Log{0xaa_address, {}, {0x01_bytes32, 0x02_bytes32}}, }; EXPECT_EQ(test::logs_hash(logs), diff --git a/test/unittests/statetest_withdrawals_test.cpp b/test/unittests/statetest_withdrawals_test.cpp new file mode 100644 index 00000000..58eb72f3 --- /dev/null +++ b/test/unittests/statetest_withdrawals_test.cpp @@ -0,0 +1,104 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace evmone; +using namespace evmone::state; +using namespace evmone::test; + +TEST(statetest_withdrawals, withdrawals_root_hash) +{ + // Input taken from https://etherscan.io/block/17826409 + constexpr std::string_view input = + R"([{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe162d9","index":"0xc13ad8","validatorIndex":"0xa2f00"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1c5c2","index":"0xc13ad9","validatorIndex":"0xa2f01"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe14f28","index":"0xc13ada","validatorIndex":"0xa2f02"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe190f2","index":"0xc13adb","validatorIndex":"0xa2f03"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1e59c","index":"0xc13adc","validatorIndex":"0xa2f04"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1bbfe","index":"0xc13add","validatorIndex":"0xa2f05"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe20974","index":"0xc13ade","validatorIndex":"0xa2f06"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe145b7","index":"0xc13adf","validatorIndex":"0xa2f07"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe11e5d","index":"0xc13ae0","validatorIndex":"0xa2f08"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe221e0","index":"0xc13ae1","validatorIndex":"0xa2f09"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe2061a","index":"0xc13ae2","validatorIndex":"0xa2f0a"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe23d22","index":"0xc13ae3","validatorIndex":"0xa2f0b"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1ab3a","index":"0xc13ae4","validatorIndex":"0xa2f0c"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe19535","index":"0xc13ae5","validatorIndex":"0xa2f0d"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0x317c537","index":"0xc13ae6","validatorIndex":"0xa2f0e"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1965f","index":"0xc13ae7","validatorIndex":"0xa2f0f"}])"; + + const auto j = json::json::parse(input); + + std::vector withdrawals; + for (const auto& withdrawal : j) + withdrawals.push_back(from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0x38cd9ae992a22b94a1582e7d0691dbef56a90cdb36bf7b11d98373f80b102c8f_bytes32); +} + +TEST(statetest_withdrawals, withdrawals_warmup_test_case) +{ + // Input taken from + // https://github.com/ethereum/tests/blob/develop/BlockchainTests/InvalidBlocks/bc4895-withdrawals/warmup.json + constexpr std::string_view input = + R"([ +{ + "index" : "0x0", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000001" +}, +{ + "index" : "0x1", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000002" +}, +{ + "index" : "0x2", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000003" +}, +{ + "index" : "0x3", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000004" +}, +{ + "index" : "0x4", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000005" +}, +{ + "index" : "0x5", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000006" +}, +{ + "index" : "0x6", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000007" +}, +{ + "index" : "0x7", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000008" +}, +{ + "index" : "0x8", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000009" +}, +{ + "index" : "0x9", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000010" +} +])"; + const auto j = json::json::parse(input); + + std::vector withdrawals; + for (const auto& withdrawal : j) + withdrawals.push_back(from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0xaa45c53e9f7d6a8362f80876029915da00b1441ef39eb9bbb74f98465ff433ad_bytes32); +} diff --git a/test/unittests/tracing_test.cpp b/test/unittests/tracing_test.cpp index 0d5054a7..20fbc6fa 100644 --- a/test/unittests/tracing_test.cpp +++ b/test/unittests/tracing_test.cpp @@ -12,6 +12,7 @@ #include using namespace testing; +using namespace evmone::test; class tracing : public Test { @@ -20,6 +21,7 @@ class tracing : public Test protected: evmone::VM& vm; + evmc::MockedHost host; std::ostringstream trace_stream; @@ -31,7 +33,6 @@ class tracing : public Test std::string trace( bytes_view code, int32_t depth = 0, uint32_t flags = 0, evmc_revision rev = EVMC_BERLIN) { - evmc::MockedHost host; evmc_message msg{}; msg.depth = depth; msg.flags = flags; @@ -169,11 +170,9 @@ TEST_F(tracing, trace) trace_stream << '\n'; EXPECT_EQ(trace(add(2, 3)), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":96,"opName":"PUSH1","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":2,"op":96,"opName":"PUSH1","gas":0xf423d,"stack":["0x3"],"memorySize":0} -{"pc":4,"op":1,"opName":"ADD","gas":0xf423a,"stack":["0x3","0x2"],"memorySize":0} -{"error":null,"gas":0xf4237,"gasUsed":0x9,"output":""} +{"pc":0,"op":96,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0x3"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":1,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0x3","0x2"],"depth":1,"refund":0,"opName":"ADD"} )"); } @@ -184,15 +183,13 @@ TEST_F(tracing, trace_stack) const auto code = push(1) + push(2) + push(3) + push(4) + OP_ADD + OP_ADD + OP_ADD; trace_stream << '\n'; EXPECT_EQ(trace(code), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":96,"opName":"PUSH1","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":2,"op":96,"opName":"PUSH1","gas":0xf423d,"stack":["0x1"],"memorySize":0} -{"pc":4,"op":96,"opName":"PUSH1","gas":0xf423a,"stack":["0x1","0x2"],"memorySize":0} -{"pc":6,"op":96,"opName":"PUSH1","gas":0xf4237,"stack":["0x1","0x2","0x3"],"memorySize":0} -{"pc":8,"op":1,"opName":"ADD","gas":0xf4234,"stack":["0x1","0x2","0x3","0x4"],"memorySize":0} -{"pc":9,"op":1,"opName":"ADD","gas":0xf4231,"stack":["0x1","0x2","0x7"],"memorySize":0} -{"pc":10,"op":1,"opName":"ADD","gas":0xf422e,"stack":["0x1","0x9"],"memorySize":0} -{"error":null,"gas":0xf422b,"gasUsed":0x15,"output":""} +{"pc":0,"op":96,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":96,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0x1","0x2"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0xf4237","gasCost":"0x3","memSize":0,"stack":["0x1","0x2","0x3"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":1,"gas":"0xf4234","gasCost":"0x3","memSize":0,"stack":["0x1","0x2","0x3","0x4"],"depth":1,"refund":0,"opName":"ADD"} +{"pc":9,"op":1,"gas":"0xf4231","gasCost":"0x3","memSize":0,"stack":["0x1","0x2","0x7"],"depth":1,"refund":0,"opName":"ADD"} +{"pc":10,"op":1,"gas":"0xf422e","gasCost":"0x3","memSize":0,"stack":["0x1","0x9"],"depth":1,"refund":0,"opName":"ADD"} )"); } @@ -203,9 +200,7 @@ TEST_F(tracing, trace_error) const auto code = bytecode{OP_POP}; trace_stream << '\n'; EXPECT_EQ(trace(code), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":80,"opName":"POP","gas":0xf4240,"stack":[],"memorySize":0} -{"error":"stack underflow","gas":0x0,"gasUsed":0xf4240,"output":""} +{"pc":0,"op":80,"gas":"0xf4240","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"POP"} )"); } @@ -216,14 +211,12 @@ TEST_F(tracing, trace_output) const auto code = push(0xabcdef) + ret_top(); trace_stream << '\n'; EXPECT_EQ(trace(code), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":98,"opName":"PUSH3","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":4,"op":96,"opName":"PUSH1","gas":0xf423d,"stack":["0xabcdef"],"memorySize":0} -{"pc":6,"op":82,"opName":"MSTORE","gas":0xf423a,"stack":["0xabcdef","0x0"],"memorySize":0} -{"pc":7,"op":96,"opName":"PUSH1","gas":0xf4234,"stack":[],"memorySize":32} -{"pc":9,"op":96,"opName":"PUSH1","gas":0xf4231,"stack":["0x20"],"memorySize":32} -{"pc":11,"op":243,"opName":"RETURN","gas":0xf422e,"stack":["0x20","0x0"],"memorySize":32} -{"error":null,"gas":0xf422e,"gasUsed":0x12,"output":"0000000000000000000000000000000000000000000000000000000000abcdef"} +{"pc":0,"op":98,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH3"} +{"pc":4,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0xabcdef"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":82,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0xabcdef","0x0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":7,"op":96,"gas":"0xf4234","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":96,"gas":"0xf4231","gasCost":"0x3","memSize":32,"stack":["0x20"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":11,"op":243,"gas":"0xf422e","gasCost":"0x0","memSize":32,"stack":["0x20","0x0"],"depth":1,"refund":0,"opName":"RETURN"} )"); } @@ -234,38 +227,36 @@ TEST_F(tracing, trace_revert) const auto code = mstore(0, 0x0e4404) + push(3) + push(29) + OP_REVERT; trace_stream << '\n'; EXPECT_EQ(trace(code), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":98,"opName":"PUSH3","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":4,"op":96,"opName":"PUSH1","gas":0xf423d,"stack":["0xe4404"],"memorySize":0} -{"pc":6,"op":82,"opName":"MSTORE","gas":0xf423a,"stack":["0xe4404","0x0"],"memorySize":0} -{"pc":7,"op":96,"opName":"PUSH1","gas":0xf4234,"stack":[],"memorySize":32} -{"pc":9,"op":96,"opName":"PUSH1","gas":0xf4231,"stack":["0x3"],"memorySize":32} -{"pc":11,"op":253,"opName":"REVERT","gas":0xf422e,"stack":["0x3","0x1d"],"memorySize":32} -{"error":"revert","gas":0xf422e,"gasUsed":0x12,"output":"0e4404"} +{"pc":0,"op":98,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH3"} +{"pc":4,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0xe4404"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":82,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0xe4404","0x0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":7,"op":96,"gas":"0xf4234","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":96,"gas":"0xf4231","gasCost":"0x3","memSize":32,"stack":["0x3"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":11,"op":253,"gas":"0xf422e","gasCost":"0x0","memSize":32,"stack":["0x3","0x1d"],"depth":1,"refund":0,"opName":"REVERT"} )"); } -TEST_F(tracing, trace_create) -{ - vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); - - trace_stream << '\n'; - EXPECT_EQ(trace({}, 2), R"( -{"depth":2,"rev":"Berlin","static":false} -{"error":null,"gas":0xf4240,"gasUsed":0x0,"output":""} -)"); -} - -TEST_F(tracing, trace_static) -{ - vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); - - trace_stream << '\n'; - EXPECT_EQ(trace({}, 2, EVMC_STATIC), R"( -{"depth":2,"rev":"Berlin","static":true} -{"error":null,"gas":0xf4240,"gasUsed":0x0,"output":""} -)"); -} +// TEST_F(tracing, trace_create) +//{ +// vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); +// +// trace_stream << '\n'; +// EXPECT_EQ(trace({}, 2), R"( +//{"depth":2,"rev":"Berlin","static":false} +//{"error":null,"gas":0xf4240,"gasUsed":0x0,"output":""} +//)"); +// } +// +// TEST_F(tracing, trace_static) +//{ +// vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); +// +// trace_stream << '\n'; +// EXPECT_EQ(trace({}, 2, EVMC_STATIC), R"( +//{"depth":2,"rev":"Berlin","static":true} +//{"error":null,"gas":0xf4240,"gasUsed":0x0,"output":""} +//)"); +// } TEST_F(tracing, trace_undefined_instruction) { @@ -274,10 +265,8 @@ TEST_F(tracing, trace_undefined_instruction) const auto code = bytecode{} + OP_JUMPDEST + "EF"; trace_stream << '\n'; EXPECT_EQ(trace(code), R"( -{"depth":0,"rev":"Berlin","static":false} -{"pc":0,"op":91,"opName":"JUMPDEST","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":1,"op":239,"opName":"0xef","gas":0xf423f,"stack":[],"memorySize":0} -{"error":"undefined instruction","gas":0x0,"gasUsed":0xf4240,"output":""} +{"pc":0,"op":91,"gas":"0xf4240","gasCost":"0x1","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"JUMPDEST"} +{"pc":1,"op":239,"gas":"0xf423f","gasCost":"0xffff","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"0xef"} )"); } @@ -299,12 +288,38 @@ TEST_F(tracing, trace_eof) vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); trace_stream << '\n'; - EXPECT_EQ(trace(eof1_bytecode(add(2, 3) + OP_STOP, 2), 0, 0, EVMC_CANCUN), R"( -{"depth":0,"rev":"Cancun","static":false} -{"pc":0,"op":96,"opName":"PUSH1","gas":0xf4240,"stack":[],"memorySize":0} -{"pc":2,"op":96,"opName":"PUSH1","gas":0xf423d,"stack":["0x3"],"memorySize":0} -{"pc":4,"op":1,"opName":"ADD","gas":0xf423a,"stack":["0x3","0x2"],"memorySize":0} -{"pc":5,"op":0,"opName":"STOP","gas":0xf4237,"stack":["0x5"],"memorySize":0} -{"error":null,"gas":0xf4237,"gasUsed":0x9,"output":""} + EXPECT_EQ(trace(bytecode{eof_bytecode(add(2, 3) + OP_STOP, 2)}, 0, 0, EVMC_PRAGUE), R"( +{"pc":0,"op":96,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0x3"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":1,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0x3","0x2"],"depth":1,"refund":0,"opName":"ADD"} +{"pc":5,"op":0,"gas":"0xf4237","gasCost":"0x0","memSize":0,"stack":["0x5"],"depth":1,"refund":0,"opName":"STOP"} +)"); +} + +TEST_F(tracing, trace_create_instruction) +{ + using namespace evmc::literals; + + vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); + + trace_stream << '\n'; + + const auto code = push(10) + push(0) + push(0) + OP_CREATE + ret_top(); + + const auto result_data = "0x60016000526001601ff3"_hex; + host.call_result.create_address = 0x1122334455667788991011223344556677889910_address; + host.call_result.output_data = result_data.data(); + host.call_result.output_size = result_data.size(); + + EXPECT_EQ(trace(code, 0, 0, EVMC_BERLIN), R"( +{"pc":0,"op":96,"gas":"0xf4240","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xf423d","gasCost":"0x3","memSize":0,"stack":["0xa"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":96,"gas":"0xf423a","gasCost":"0x3","memSize":0,"stack":["0xa","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":240,"gas":"0xf4237","gasCost":"0x7d00","memSize":0,"stack":["0xa","0x0","0x0"],"depth":1,"refund":0,"opName":"CREATE"} +{"pc":7,"op":96,"gas":"0x3b14","gasCost":"0x3","memSize":32,"stack":["0x1122334455667788991011223344556677889910"],"returnData":"0x60016000526001601ff3","depth":1,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":82,"gas":"0x3b11","gasCost":"0x3","memSize":32,"stack":["0x1122334455667788991011223344556677889910","0x0"],"returnData":"0x60016000526001601ff3","depth":1,"refund":0,"opName":"MSTORE"} +{"pc":10,"op":96,"gas":"0x3b0e","gasCost":"0x3","memSize":32,"stack":[],"returnData":"0x60016000526001601ff3","depth":1,"refund":0,"opName":"PUSH1"} +{"pc":12,"op":96,"gas":"0x3b0b","gasCost":"0x3","memSize":32,"stack":["0x20"],"returnData":"0x60016000526001601ff3","depth":1,"refund":0,"opName":"PUSH1"} +{"pc":14,"op":243,"gas":"0x3b08","gasCost":"0x0","memSize":32,"stack":["0x20","0x0"],"returnData":"0x60016000526001601ff3","depth":1,"refund":0,"opName":"RETURN"} )"); } diff --git a/test/utils/CMakeLists.txt b/test/utils/CMakeLists.txt index 12a11228..cd27d4c4 100644 --- a/test/utils/CMakeLists.txt +++ b/test/utils/CMakeLists.txt @@ -4,9 +4,9 @@ find_package(intx CONFIG REQUIRED) -add_library(testutils INTERFACE) +add_library(testutils STATIC) add_library(evmone::testutils ALIAS testutils) -target_link_libraries(testutils INTERFACE evmc::evmc_cpp) +target_link_libraries(testutils PUBLIC evmc::evmc_cpp) target_include_directories(testutils INTERFACE ${PROJECT_SOURCE_DIR} ${evmone_private_include_dir}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) @@ -17,3 +17,4 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) utils.hpp ) endif() +target_sources(testutils PRIVATE utils.cpp) diff --git a/test/utils/bytecode.hpp b/test/utils/bytecode.hpp index cab98883..8ee590d2 100644 --- a/test/utils/bytecode.hpp +++ b/test/utils/bytecode.hpp @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -11,6 +12,8 @@ #include #include +namespace evmone::test +{ struct bytecode; inline bytecode push(uint64_t n); @@ -31,8 +34,8 @@ struct bytecode : bytes bytecode(Opcode opcode) : bytes{uint8_t(opcode)} {} - template >> + template + requires(std::is_convertible_v) bytecode(T hex) : bytes{from_spaced_hex(hex).value()} {} @@ -84,36 +87,112 @@ inline bytecode operator*(int n, Opcode op) } template -inline typename std::enable_if_t || std::is_same_v, bytes> -big_endian(T value) + requires(std::is_same_v || std::is_same_v) +inline bytes big_endian(T value) { return {static_cast(value >> 8), static_cast(value)}; } -inline bytecode eof_header( - uint8_t version, uint16_t code_size, uint16_t max_stack_height, uint16_t data_size) +struct eof_bytecode { - bytecode out{bytes{0xEF, 0x00, version}}; - out += "01" + big_endian(uint16_t{4}); // type header - out += "02"_hex + big_endian(uint16_t{1}) + big_endian(code_size); - out += "03" + big_endian(data_size); - out += "00"; - out += "0000"_hex + big_endian(max_stack_height); // type section - return out; -} +private: + std::vector m_codes; + std::vector m_types; + bytecode m_data; + uint16_t m_data_size = 0; + std::vector m_containers; + + /// Constructs EOF header bytes + bytecode header() const + { + assert(!m_codes.empty()); + assert(m_codes[0].size() <= std::numeric_limits::max()); + assert(m_data.size() <= std::numeric_limits::max()); + assert(m_codes.size() == m_types.size()); + assert(m_containers.size() <= std::numeric_limits::max()); -inline bytecode eof1_header(uint16_t code_size, uint16_t max_stack_height, uint16_t data_size = 0) -{ - return eof_header(1, code_size, max_stack_height, data_size); -} + constexpr uint8_t version = 0x01; + + bytecode out{bytes{0xEF, 0x00, version}}; + + // type header + const auto types_size = static_cast(m_types.size() * 4); + out += "01" + big_endian(types_size); + + // codes header + const auto code_count = static_cast(m_codes.size()); + out += "02"_hex + big_endian(code_count); + for (const auto& code : m_codes) + { + const auto code_size = static_cast(code.size()); + out += big_endian(code_size); + } + + // containers header + if (!m_containers.empty()) + { + const auto container_count = static_cast(m_containers.size()); + out += "03"_hex + big_endian(container_count); + for (const auto& container : m_containers) + { + assert(container.size() <= std::numeric_limits::max()); + const auto container_size = static_cast(container.size()); + out += big_endian(container_size); + } + } -inline bytecode eof1_bytecode(bytecode code, uint16_t max_stack_height = 0, bytecode data = {}) + // data header + const auto data_size = + (m_data_size == 0 ? static_cast(m_data.size()) : m_data_size); + out += "04" + big_endian(data_size); + out += "00"; // terminator + + // types section + for (const auto& type : m_types) + out += bytes{type.inputs, type.outputs} + big_endian(type.max_stack_height); + return out; + } + +public: + explicit eof_bytecode(bytecode code, uint16_t max_stack_height = 0) + : m_codes{std::move(code)}, m_types{{0, 0x80, max_stack_height}} + {} + + auto& code(bytecode c, uint8_t inputs, uint8_t outputs, uint16_t max_stack_height) + { + m_codes.emplace_back(std::move(c)); + m_types.emplace_back(inputs, outputs, max_stack_height); + return *this; + } + + auto& data(bytecode d, uint16_t size = 0) + { + m_data = std::move(d); + m_data_size = size; + return *this; + } + + auto& container(bytecode c) + { + m_containers.emplace_back(std::move(c)); + return *this; + } + + operator bytecode() const + { + bytecode out{header()}; + for (const auto& code : m_codes) + out += code; + for (const auto& container : m_containers) + out += container; + out += m_data; + return out; + } +}; + +inline bytecode push0() { - assert(code.size() <= std::numeric_limits::max()); - assert(data.size() <= std::numeric_limits::max()); - return eof1_header(static_cast(code.size()), max_stack_height, - static_cast(data.size())) + - code + data; + return OP_PUSH0; } inline bytecode push(bytes_view data) @@ -214,6 +293,11 @@ inline bytecode byte(bytecode a, bytecode n) return a + n + OP_BYTE; } +inline bytecode mload(bytecode index) +{ + return index + OP_MLOAD; +} + inline bytecode mstore(bytecode index) { return index + OP_MSTORE; @@ -234,6 +318,11 @@ inline bytecode mstore8(bytecode index, bytecode value) return value + index + OP_MSTORE8; } +inline bytecode mcopy(bytecode dst, bytecode src, bytecode length) +{ + return length + src + dst + OP_MCOPY; +} + inline bytecode jump(bytecode target) { return target + OP_JUMP; @@ -256,12 +345,28 @@ inline bytecode rjumpi(int16_t offset, bytecode condition) inline bytecode rjumpv(const std::initializer_list offsets, bytecode condition) { - bytecode ret = condition + OP_RJUMPV + static_cast(offsets.size()); + assert(offsets.size() > 0); + bytecode ret = condition + OP_RJUMPV + static_cast(offsets.size() - 1); for (const auto offset : offsets) ret += bytecode{big_endian(offset)}; return ret; } +inline bytecode dataloadn(uint16_t index) +{ + return OP_DATALOADN + big_endian(index); +} + +inline bytecode callf(uint16_t target) +{ + return OP_CALLF + big_endian(target); +} + +inline bytecode jumpf(uint16_t target) +{ + return OP_JUMPF + big_endian(target); +} + inline bytecode ret(bytecode index, bytecode size) { return size + index + OP_RETURN; @@ -277,6 +382,12 @@ inline bytecode ret(bytecode c) return c + ret_top(); } +inline bytecode returncontract( + uint8_t container_index, bytecode aux_data_offset, bytecode aux_data_size) +{ + return aux_data_size + aux_data_offset + OP_RETURNCONTRACT + bytecode{bytes{container_index}}; +} + inline bytecode revert(bytecode index, bytecode size) { return size + index + OP_REVERT; @@ -302,9 +413,24 @@ inline bytecode calldatasize() return OP_CALLDATASIZE; } -inline bytecode calldatacopy(bytecode src, bytecode dst, bytecode size) +inline bytecode calldatacopy(bytecode dst, bytecode src, bytecode size) +{ + return std::move(size) + std::move(src) + std::move(dst) + OP_CALLDATACOPY; +} + +inline bytecode returndataload(bytecode index) +{ + return index + OP_RETURNDATALOAD; +} + +inline bytecode returndatasize() +{ + return OP_RETURNDATASIZE; +} + +inline bytecode returndatacopy(bytecode dst, bytecode src, bytecode size) { - return std::move(size) + std::move(dst) + std::move(src) + OP_CALLDATACOPY; + return std::move(size) + std::move(src) + std::move(dst) + OP_RETURNDATACOPY; } inline bytecode sstore(bytecode index, bytecode value) @@ -312,11 +438,36 @@ inline bytecode sstore(bytecode index, bytecode value) return value + index + OP_SSTORE; } +inline bytecode sstore(bytecode index) +{ + return index + OP_SSTORE; +} + inline bytecode sload(bytecode index) { return index + OP_SLOAD; } +inline bytecode tstore(bytecode index, bytecode value) +{ + return value + index + OP_TSTORE; +} + +inline bytecode tload(bytecode index) +{ + return index + OP_TLOAD; +} + +inline bytecode blockhash(bytecode number) +{ + return number + OP_BLOCKHASH; +} + +inline bytecode blobhash(bytecode index) +{ + return index + OP_BLOBHASH; +} + template struct call_instruction { @@ -340,8 +491,8 @@ struct call_instruction template - typename std::enable_if::type value( - bytecode v) + requires(k == OP_CALL || k == OP_CALLCODE) + call_instruction value(bytecode v) { m_value = std::move(v); return *this; @@ -391,6 +542,59 @@ inline call_instruction callcode(bytecode address) return call_instruction{std::move(address)}; } +template +struct extcall_instruction +{ +private: + bytecode m_address = 0; + bytecode m_value = 0; + bytecode m_input = 0; + bytecode m_input_size = 0; + +public: + explicit extcall_instruction(bytecode address) : m_address{std::move(address)} {} + + + template + requires(k == OP_EXTCALL) + extcall_instruction& value(bytecode v) + { + m_value = std::move(v); + return *this; + } + + auto& input(bytecode index, bytecode size) + { + m_input = std::move(index); + m_input_size = std::move(size); + return *this; + } + + operator bytecode() const + { + auto code = bytecode{}; + if constexpr (kind == OP_EXTCALL) + code += m_value; + code += m_input_size + m_input + m_address + kind; + return code; + } +}; + +inline extcall_instruction extdelegatecall(bytecode address) +{ + return extcall_instruction{std::move(address)}; +} + +inline extcall_instruction extstaticcall(bytecode address) +{ + return extcall_instruction{std::move(address)}; +} + +inline extcall_instruction extcall(bytecode address) +{ + return extcall_instruction{std::move(address)}; +} + template struct create_instruction @@ -400,6 +604,8 @@ struct create_instruction bytecode m_input = 0; bytecode m_input_size = 0; bytecode m_salt = 0; + uint8_t m_container_index = 0; + bytecode m_initcode_hash = 0; public: auto& value(bytecode v) @@ -416,18 +622,37 @@ struct create_instruction } template - typename std::enable_if::type salt(bytecode salt) + requires(k == OP_CREATE2 || k == OP_EOFCREATE) + create_instruction& salt(bytecode salt) { m_salt = std::move(salt); return *this; } + template + requires(k == OP_EOFCREATE) + create_instruction& container(uint8_t index) + { + m_container_index = index; + return *this; + } + operator bytecode() const { bytecode code; if constexpr (kind == OP_CREATE2) code += m_salt; - code += m_input_size + m_input + m_value + kind; + else if constexpr (kind == OP_EOFCREATE) + code += m_input_size + m_input + m_salt; + + if constexpr (kind == OP_CREATE || kind == OP_CREATE2) + code += m_input_size + m_input; + + code += m_value; + + code += bytecode{kind}; + if constexpr (kind == OP_EOFCREATE) + code += bytecode{bytes{m_container_index}}; // immediate argument return code; } }; @@ -442,10 +667,14 @@ inline auto create2() return create_instruction{}; } +inline auto eofcreate() +{ + return create_instruction{}; +} inline std::string hex(Opcode opcode) noexcept { - return hex(static_cast(opcode)); + return evmc::hex(static_cast(opcode)); } inline std::string decode(bytes_view bytecode) @@ -458,22 +687,23 @@ inline std::string decode(bytes_view bytecode) { s += std::string{" + OP_"} + name; - if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32) + if (size_t imm_size = evmone::instr::traits[opcode].immediate_size; imm_size > 0) { - const auto push_data_start = it + 1; - const auto push_data_size = - std::min(static_cast(opcode - OP_PUSH1 + 1), - static_cast(bytecode.end() - push_data_start)); - if (push_data_size != 0) + const auto imm_start = it + 1; + imm_size = std::min(imm_size, static_cast(bytecode.end() - imm_start)); + + if (imm_size != 0) { - s += " + \"" + hex({&*push_data_start, push_data_size}) + '"'; - it += push_data_size; + s += " + \"" + evmc::hex({&*imm_start, imm_size}) + '"'; + it += imm_size; } } } else - s += " + \"" + hex(opcode) + '"'; + s += " + \"" + evmc::hex(opcode) + '"'; } return s; } + +} // namespace evmone::test diff --git a/test/utils/utils.cpp b/test/utils/utils.cpp new file mode 100644 index 00000000..a3abe5b6 --- /dev/null +++ b/test/utils/utils.cpp @@ -0,0 +1,56 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "utils.hpp" + +namespace evmone::test +{ + +evmc_revision to_rev(std::string_view s) +{ + if (s == "Frontier") + return EVMC_FRONTIER; + if (s == "Homestead") + return EVMC_HOMESTEAD; + if (s == "Tangerine Whistle" || s == "EIP150") + return EVMC_TANGERINE_WHISTLE; + if (s == "Spurious Dragon" || s == "EIP158") + return EVMC_SPURIOUS_DRAGON; + if (s == "Byzantium") + return EVMC_BYZANTIUM; + if (s == "Constantinople") + return EVMC_CONSTANTINOPLE; + if (s == "Petersburg" || s == "ConstantinopleFix") + return EVMC_PETERSBURG; + if (s == "Istanbul") + return EVMC_ISTANBUL; + if (s == "Berlin") + return EVMC_BERLIN; + if (s == "London" || s == "ArrowGlacier") + return EVMC_LONDON; + if (s == "Paris" || s == "Merge") + return EVMC_PARIS; + if (s == "Shanghai") + return EVMC_SHANGHAI; + if (s == "Cancun") + return EVMC_CANCUN; + if (s == "Prague") + return EVMC_PRAGUE; + if (s == "Osaka") + return EVMC_OSAKA; + throw std::invalid_argument{"unknown revision: " + std::string{s}}; +} + +RevisionSchedule to_rev_schedule(std::string_view s) +{ + if (s == "ShanghaiToCancunAtTime15k") + return {EVMC_SHANGHAI, EVMC_CANCUN, 15'000}; + if (s == "CancunToPragueAtTime15k") + return {EVMC_CANCUN, EVMC_PRAGUE, 15'000}; + + const auto single_rev = to_rev(s); + return {single_rev, single_rev, 0}; +} + +} // namespace evmone::test diff --git a/test/utils/utils.hpp b/test/utils/utils.hpp index 0c5ff7c4..25aac329 100644 --- a/test/utils/utils.hpp +++ b/test/utils/utils.hpp @@ -3,20 +3,57 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include +#include +namespace evmone::test +{ using evmc::bytes; using evmc::bytes_view; using evmc::from_hex; using evmc::from_spaced_hex; using evmc::hex; +/// The EVM revision schedule based on timestamps. +struct RevisionSchedule +{ + /// The revision of the first block. + evmc_revision genesis_rev = EVMC_FRONTIER; + + /// The final revision to transition to. + evmc_revision final_rev = genesis_rev; + + /// The timestamp of the transition to the final revision. + int64_t transition_time = 0; + + /// Returns the specific revision for the given timestamp. + [[nodiscard]] evmc_revision get_revision(int64_t timestamp) const noexcept + { + return timestamp >= transition_time ? final_rev : genesis_rev; + } +}; + +/// Translates tests fork name to EVM revision +evmc_revision to_rev(std::string_view s); + +/// Translates tests fork name to the EVM revision schedule. +RevisionSchedule to_rev_schedule(std::string_view s); + /// Converts a string to bytes by casting individual characters. inline bytes to_bytes(std::string_view s) { return {s.begin(), s.end()}; } +/// Convert address to 32-byte value left-padding with 0s. +inline evmc::bytes32 to_bytes32(const evmc::address& addr) +{ + evmc::bytes32 addr32; + std::copy_n(addr.bytes, sizeof(addr), &addr32.bytes[sizeof(addr32) - sizeof(addr)]); + return addr32; +} + /// Produces bytes out of string literal. inline bytes operator""_b(const char* data, size_t size) { @@ -27,3 +64,5 @@ inline bytes operator""_hex(const char* s, size_t size) { return from_spaced_hex({s, size}).value(); } + +} // namespace evmone::test